Entity Framework細節追蹤


 為了加深對EF特性的了解,so,寫了一些測試代碼。測試結果也許對實際項目沒什么用處,但是對理解EF的相關機制還是有一定幫助的。本文可能會不定期更新(加入新的測試用例=。=)。

一、事務


 直接看代碼。

1、所有SaveChange包裹在一個TransactionScope里面。

 1 [TestMethod]
 2 public void TestMethod1()
 3 {
 4     using (var entities = new SysProcessEntities())
 5     {
 6         using (TransactionScope scope = new TransactionScope())
 7         {
 8             try
 9             {
10                 var test = entities.SysUser.Where(o => o.OrganizationID == 1).ToList();
11                 var test2 = entities.SysRole.ToList();
12 
13                 test[0].Code = "admin1";
14                 test2[0].Name = "dddd";
15                 entities.SaveChanges();
16 
17                 test[0].Code = "admin";
18                 test2[0].Name = "全功能";
19                 entities.SaveChanges();
20 
21                 scope.Complete();
22             }
23             catch (Exception e)
24             {
25 
26             }
27         }
28     }
29 }

結果:

2、在1的基礎上去掉TransactionScope

 1 [TestMethod]
 2 public void TestMethod1()
 3 {
 4     using (var entities = new SysProcessEntities())
 5     {
 6         var test = entities.SysUser.Where(o => o.OrganizationID == 1).ToList();
 7         var test2 = entities.SysRole.ToList();
 8 
 9         test[0].Code = "admin1";
10         test2[0].Name = "dddd";
11         entities.SaveChanges();
12 
13         test[0].Code = "admin";
14         test2[0].Name = "全功能";
15         entities.SaveChanges();
16     }
17 }

結果:

3、注釋掉2的第9行和第13行,檢查單獨的一條語句是否自帶事務。

 1 [TestMethod]
 2 public void TestMethod1()
 3 {
 4     using (var entities = new SysProcessEntities())
 5     {
 6         var test = entities.SysUser.Where(o => o.OrganizationID == 1).ToList();
 7         var test2 = entities.SysRole.ToList();
 8 
 9         //test[0].Code = "admin1";
10         test2[0].Name = "dddd";
11         entities.SaveChanges();
12 
13         //test[0].Code = "admin";
14         test2[0].Name = "全功能";
15         entities.SaveChanges();
16     }
17 }

結果:

4、直接寫Sql-"update [SysProcess].[dbo].[SysRole] set [name]='全功能' where ID=1",在查詢分析器中執行,結果:

5、將1改為跨數據庫

 1 [TestMethod]
 2 public void TestMethod1()
 3 {
 4     using (TransactionScope scope = new TransactionScope())
 5     {
 6         try
 7         {
 8             using (var entities = new SysProcessEntities())
 9             {
10                 var test = entities.SysUser.Where(o => o.OrganizationID == 1).ToList();
11                 var test2 = entities.SysRole.ToList();
12 
13                 test[0].Code = "admin1";
14                 test2[0].Name = "dddd";
15                 entities.SaveChanges();
16 
17                 test[0].Code = "admin";
18                 test2[0].Name = "全功能";
19                 entities.SaveChanges();
20             }
21             using (var entities = new DistributionEntities())
22             {
23                 var test = entities.VIPCard.ToList();
24                 test[0].Sex = true;
25                 entities.SaveChanges();
26             }
27 
28             scope.Complete();
29         }
30         catch (Exception e)
31         {
32             string msg = e.Message;
33         }
34     }
35 }

結果:

於是整個世界美好了……

二、AsNoTracking


 注意AsNoTracking要寫在最終返回數據的那行代碼中才有用,看代碼:

 1 [TestMethod]
 2 public void TestMethod9()
 3 {
 4     using (var entities = new SysProcessEntities())
 5     {
 6         var ubs = entities.UserBrand.AsNoTracking();
 7         var obs = entities.OrganizationBrand.Where(ob => ob.OrganizationID == 1).AsNoTracking();
 8         var brands = from ub in ubs
 9                         from ob in obs
10                         where ub.BrandID == ob.BrandID
11                         select ub.BrandID;
12         var test = entities.ProBrand.Where(b => brands.Contains(b.ID)).ToList();
13         Assert.AreEqual(EntityState.Detached, entities.Entry(test[0]).State);
14     }
15 }

結果:

so,using之中應該這么寫:

var ubs = entities.UserBrand;
var obs = entities.OrganizationBrand.Where(ob => ob.OrganizationID == 1);
var brands = from ub in ubs
                from ob in obs
                where ub.BrandID == ob.BrandID
                select ub.BrandID;
var test = entities.ProBrand.Where(b => brands.Contains(b.ID)).AsNoTracking().ToList();
Assert.AreEqual(EntityState.Detached, entities.Entry(test[0]).State);

三、DbSet.Local


 繼續看代碼:

1、

1 public void TestMethod10()
2 {
3     var entities = new SysProcessEntities();
4     var t1 = entities.ProBoduan.ToList();
5     var t2 = entities.ProBoduan.ToList();
6     Assert.AreEqual(t1[0],t2[0]);
7 }

大家以為t1[0]是否等於t2[0]?答案是true。可是作為引用類型,我並沒有重載它的Equals方法,照理應該為false才對呀。修改下測試代碼:

1 public void TestMethod10()
2 {
3     var entities = new SysProcessEntities();
4     var t1 = entities.ProBoduan.ToList();
5     var t2 = entities.ProBoduan.ToList();
6     string name = t2[0].Name;
7     t1[0].Name = "隨便取個名用來測試";
8     Assert.AreEqual(name, t2[0].Name);
9 }

結果:。可知,t1[0]和t2[0]指向的是同一個對象,即t1和t2指向同一個數組地址,也就是entities.ProBoduan.Local指向的地址。不過數據庫中,仍執行了兩次取數操作。

2、then,加入AsNoTracking試試看:

public void TestMethod10()
{
    var entities = new SysProcessEntities();
    var t1 = entities.ProBoduan.AsNoTracking().ToList();
    var t2 = entities.ProBoduan.AsNoTracking().ToList();
    string name = t2[0].Name;
    t1[0].Name = "隨便取個名用來測試";
    Assert.AreEqual(name, t2[0].Name); //true,t1的更改不影響t2,表明t1和t2指向不同地址
}

此時entities.ProBoduan.Local.Count為0。

3、

1 public void TestMethod10()
2 {
3     var entities = new SysProcessEntities();    
4     var t2 = entities.ProBoduan.FirstOrDefault();
5     var count1 = entities.ProBoduan.Local.Count;//1
6     var t1 = entities.ProBoduan.ToList();
7     var count2 = entities.ProBoduan.Local.Count;//3
8 }

將第4行和第6行換下位置。

1 public void TestMethod10()
2 {
3     var entities = new SysProcessEntities();
4     var t1 = entities.ProBoduan.ToList();
5     var count1 = entities.ProBoduan.Local.Count;//3   
6     var t2 = entities.ProBoduan.FirstOrDefault();
7     var count2 = entities.ProBoduan.Local.Count;//3
8 }

四、AutoDetectChangesEnabled & ValidateOnSaveEnabled


 

 1 public void TestMethod9()
 2 {
 3     using (var entities = new SysProcessEntities())
 4     {
 5         entities.Configuration.AutoDetectChangesEnabled = false;
 6         var ubs = entities.UserBrand;
 7         var obs = entities.OrganizationBrand.Where(ob => ob.OrganizationID == 1);
 8         var brands = from ub in ubs
 9                         from ob in obs
10                         where ub.BrandID == ob.BrandID
11                         select ub.BrandID;
12         var test = entities.ProBrand.Where(b => brands.Contains(b.ID)).ToList();
13         var t1 = entities.Entry(test[0]).State;
14         test[0].Description = "ggggg";
15         var t2 = entities.Entry(test[0]).State;
16         entities.ProBrand.Remove(test[0]);
17     }
18 }

t1、t2皆為EntityState.Unchanged,原因是entities.Configuration.AutoDetectChangesEnabled = false;若設為true,那么t2將為EntityState.Modified。注意此項設置對Added和Deleted沒影響。當我們循環Add大批量數據到上下文中時,設為false將對性能有非常大的提升;該屬性設置對循環修改已跟蹤實體的屬性(entity.Property = XXXX)關系不大,大批量修改已跟蹤實體的屬性的效率我測試過,非常快(不管是true還是false;當然設為false,然后修改實體屬性,沒什么意義)。 

猜測:ValidateOnSaveEnabled表示在SaveChanges()時是否根據映射文件等判斷實體是否符合規則(如Key是否被改變,默認已跟蹤實體是不能手動改變Key值的等),以后測試!

轉載請注明本文出處:http://www.cnblogs.com/newton/archive/2013/06/03/3115603.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM