為了加深對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