当前位置: 代码迷 >> .NET Framework >> Entity Framework 学习高级篇—改进EF代码的方法
  详细解决方案

Entity Framework 学习高级篇—改进EF代码的方法

热度:47   发布时间:2016-05-02 00:17:11.0
Entity Framework 学习高级篇—改善EF代码的方法

本节,我们将介绍一些改善EF代码的相关方法,如NoTracking,GetObjectByKey, Include等。

l???????? MergeOption.NoTracking

当我们只需要读取某些数据而不需要删除、更新的时候,可以指定使用MergeOption.NoTracking的方式来执行只读查询(EF默认的方式是AppendOnly)。当指定使用NoTracking来进行只读查询时,与实体相关的引用实体不会被返回,它们会被自动设置为null。因此,使用NoTracking可以提升查询的性能。示例代码如下:

[Test]

??????? public void NoTrackingTest()

??????? {

??????????? using (var db = new NorthwindEntities1())

??????????? {

//针对Customers查询将使用MergeOption.NoTracking

??????????????? db.Customers.MergeOption = MergeOption.NoTracking;

??????????????? var cust = db.Customers.Where(c => c.City == "London");

??????????????? foreach (var c in cust)

??????????????????? Console.WriteLine(c.CustomerID);

?

??????????????? //也可以这样写

??????????????? //var cust1 = ((ObjectQuery<Customers>)cust).Execute(MergeOption.NoTracking);

?

??????????????? //Esql写法

??????????????? //string esql = "select value c from customers as c where c.CustomerID='ALFKI'";

??????????????? //db.CreateQuery<Customers>(esql).Execute(MergeOption.NoTracking).FirstOrDefault();

?

??????????? }

??????? }

?

l???????? GetObjectByKey/First

GetObjectByKey

在EF中,使用GetObjectByKey方法获取数据时,它首先会查询是否有缓存,如果有缓存则从缓存中返回需要的实体。如果没有则查询数据库,返回需要的实体,并添加在缓存中以便下次使用。

First: 总从数据库中提取需要的实体。

因此,我们应在合适的地方选择GetObjectByKey方法来获取数据,以减少对数据库的访问提升性能。示例代码如下:

[Test]

?????? ?public void GetByKeyTest()

??????? {

??????????? using (var db = new NorthwindEntities1())

??????????? {

??????????????? //从数据库中提取数据

??????????????? var cst = db.Customers.First(c => c.CustomerID == "ALFKI");

??????????????? Console.WriteLine(cst.CustomerID);

?

??????????????? //将从缓存中提取数据

??????????????? EntityKey key = new EntityKey("NorthwindEntities1.Customers", "CustomerID", "ALFKI");

??????????????? var cst1 = db.GetObjectByKey(key) as Customers;

??????????????? Console.WriteLine(cst1.CustomerID);

?

?

???????????}

??????? }

此外,需要注意的是如果GetObjectByKey没有获取到符合条件的数据,那么它会抛异常。为了避免此情况发生,在有可能出现异常的地方,我们应该使用TryGetObjectByKey方法。TryGetObjectByKey方法获取数据的方式和GetObjectByKey类似,只是当没有取到符合条件的数据时,TryGetObjectByKey会返回null而不是抛异常。示例代码如下:

[Test]

??????? public void TryGetByKeyTest()

??????? {

??????????? using (var db = new NorthwindEntities1())

??????????? {

?

??????????????? //没有符合条件的数据会有异常抛出

??????????????? EntityKey key = new EntityKey("NorthwindEntities1.Customers", "CustomerID", "♂风车车.Net");

??????????????? var cst = db.GetObjectByKey(key) as Customers;

??????????????? Console.WriteLine(cst.CustomerID);

?

??????????????? //没有符合条件的数据会有返回null

??????????????? EntityKey key1 = new EntityKey("NorthwindEntities1.Customers", "CustomerID", "♂风车车.Net");

??????????????? Object cst1 = null;

??????????????? db.TryGetObjectByKey(key1, out cst1);

??????????????? if (cst1 != null)

??????????????????? Console.WriteLine(((Customers)cst1).CustomerID);

?

??????????? }

??????? }

?

l???????? First /FirstOrDefault

First: 当我们使用First来获取数据,如果没有符合条件的数据,那么我们的代码将会抛出异常。

FirstOrDefault: 当我们使用FirstOrDefault来获取的数据,如果没有符合条件的数据,那么它将返回null。

显然,对于一个良好的代码,是对可以预见的异常进行处理,而不是等它自己抛出来。示例代码如下:

[Test]

??????? public void FirstTest()

??????? {

??????????? using (var db = new NorthwindEntities1())

??????????? {

?

??????????????? //抛异常的代码

??????????????? var cst = db.Customers.First(c => c.CustomerID == "♂风车车.Net");

??????????????? Console.WriteLine(cst.CustomerID);//此处将出抛异常

???????????????

??????????????? //推荐的使用如下代码:

??????????????? var cst1 = db.Customers.FirstOrDefault(c => c.CustomerID == "♂风车车.Net");

??????????????? if (cst1 != null)

??????????????????? Console.WriteLine(cst1.CustomerID);

??????????? }

??????? }

l???????? 延迟加载/Include

EF不支持实体的部分属性延迟加载,但它支持实体关系的延迟加载。默认情况,实体的关系是不会加载。如下代码:

[Test]

??????? public void IncludeTest()

??????? {

???? ???????using (var db = new NorthwindEntities1())

??????????? {

?????????????? var csts = db.Customers;

??????????????? foreach (var c in csts)

??????????????? {

??????????????????? Console.WriteLine(c.CustomerID);

??????????????????? foreach (var o in c.Orders)

??????????????????????? Console.WriteLine("?? " + o.OrderID);

??????????????? }

??????????? }

??????? }

上述代码中,因为Orders没有被加载,所以在输出Orders的时候,是不会有任何输出的。

当我们需要加载某些关联的关系时,可是用Include方法,如下代码所示:

[Test]

??????? public void IncludeTest()

??????? {

??????????? using (var db = new NorthwindEntities1())

??????????? {

?????????????? var csts = db.Customers.Include("Orders");

??????????????? foreach (var c in csts)

??????????????? {

??????????????????? Console.WriteLine(c.CustomerID);

??????????????????? foreach (var o in c.Orders)

??????????????????????? Console.WriteLine("?? " + o.OrderID);

??????????????? }

??????????? }

??????? }

上述代码中,Customers关联的Orders将被加载。

l???????? CompiledQuery

提供对查询的编译和缓存以供重新使用。当相同的查询需要执行很多遍的时候,那么我们可以使用ComplieQuery将查询的语句进行编译以便下次使用,这样可以免去对同一语句的多次处理,从而改善性能。

示例代码如下:

[Test]

???? ???public void ComplieTest()

??????? {

??????????? using (var db = new NorthwindEntities1())

??????????? {

??????????????? //对查询进行编译

??????????????? var customer = CompiledQuery.Compile<NorthwindEntities1, IQueryable<Customers>>(

??????????????????? (database) => database.Customers.Where(c => c.City == "London"));

???????????????

??????????????? //执行20次相同的查询

??????????????? for (int i = 0; i < 20; i++)

??????????????? {

??????????????????? DateTime dt = System.DateTime.Now;

??????????????????? foreach (var c in customer(db))

??????????????????????? Console.WriteLine(c.CustomerID);

??????????????????? Console.WriteLine(DateTime.Now.Subtract(dt).TotalMilliseconds);

???????? ???????????Console.WriteLine("---------------------------------------------------");

??????????????? }

?

??????????? ?}

?? }

l???????? 存储模型视图

EF中,当执行实体查询的时候,运行时首先将实体模型转换成ESQL视图,而ESQL视图则是根据msl文件来生成相应的代码。此外,ESQL视图包含了相应的查询语句。ESQL视图被创建后将在应用程序域中进行缓存以便下次使用。这个运行时生成存储模型视图是比较耗时的过程。

为了,免去运行时生成存储模型视图,我们可以预先产生这个的存储模型视图。具体步骤如下:

首先,使用EdmGen2来产生存储模型视图,相应的命令如下:

Edmgen2 /ViewGen cs NorthwindEntites.edmx

执行此命令后,edmgen2会在当前目录下生成一个名为NorthwindEntites.GeneratedViews.cs这个文件,就是我们要使用的存储模型视图文件。

将此文件添加到项目中就行,其他的代码不需要改变,EF会自动调用此视图文件。如下示例代码:

[Test]

??????? public void ViewTest()

??????? {

??????????? using (var db = new NorthwindEntities1())

??????????? {

??????????????? var suppliers = db.Suppliers;

??????????????? foreach (var s in suppliers)

??????????????????? Console.WriteLine(s.ContactName);

??????????? }

??????? }

没有使用存储模型视图的情况是:

1 passed, 0 failed, 0 skipped, took 7.09 seconds.

项目中添加了NorthwindEntites.GeneratedViews.cs文件,执行情况是:

1 passed, 0 failed, 0 skipped, took 5.38 seconds.

可见,使用了存储模型视图的确是提高了性能。

l???????? 冲突处理

EF中,默认情况并不会检查并发冲突。因为EF实现的是乐观的并发模式,当有并发的冲突发生时,将会抛出Optimistic Concurrency Exception异常。我们可以通过使用RefreshMode这个枚举来指定当发生冲突时如何处理。

RefreshMode有两中枚举值:

ClientsWins: 当提交修改,更新数据库中的值。

StoreWins: 放弃修改,使用数据库中的值。

示例代码片段如下:

var db2 = new NorthwindEntities1();

??????????? var customer2 = db2.Customers.FirstOrDefault(c => c.CustomerID == "2009");

??????????? if (customer2 != null)

?????????? ?{

??????????????? customer2.ContactName = "♂风车车.Net";

??????????????? customer2.City = "CD";

??????????????? customer2.Region = "GX";

??????????? }

??????????? try

??????????? {

??????????????? db2.SaveChanges();

??????????? }

??????????? catch (OptimisticConcurrencyException ex) //捕获到冲突,则进行相应的处理

??????????? {

??????????????? db2.Refresh(RefreshMode.ClientWins, customer2);

??????????????? db2.SaveChanges();

??????????? }

上述代码片段,只是说明怎么处理并发冲突,不是具体的并发。(ps:本来是准备开个线程来模拟并发的,但是始终没成功,没明白什么原因,望高人指点呢!)

  相关解决方案