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