EF 优化

系统 1577 0

本文介绍一些改善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:本来是准备开个线程来模拟并发的,但是始终没成功,没明白什么原因,望高人指点呢!)

出处:
http://www.cnblogs.com/xray2005

EF 优化


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论