Entity Framework:如果允许模型处于非法状态,在某些场景下,记得清空DbContext
背景
之前写过两篇文章介绍模型的合法性:
今天讨论的问题其实是关于“主键映射”的,只是其中还涉及一种决策:“允许模型处于非常状态”。
测试代码
1 public static void Do() 2 { 3 Database.SetInitializer<MyDbContext>(new DropCreateDatabaseAlways<MyDbContext>()); 4 5 using (var context = new MyDbContext()) 6 { 7 /****************添加一个Note*****************/ 8 var note = new Note { Name = "合法名字" }; 9 context.Set<Note>().Add(note); 10 context.SaveChanges(); 11 /****************添加一个Note*****************/ 12 13 try 14 { 15 /****************让Note处于非法状态*****************/ 16 var firstNote = context.Set<Note>().First(); 17 firstNote.Name = "非法名称"; 18 if (firstNote.Name == "非法名称") 19 { 20 throw new InvalidOperationException("非法名称"); 21 } 22 /****************让Note处于非法状态*****************/ 23 } 24 catch 25 { 26 //这里会出现BUG,显示的还是非法名字。 27 Console.WriteLine(context.Set<Note>().First().Name); 28 29 //清空DbContext以后就对了。 30 foreach (var entity in context.ChangeTracker.Entries()) 31 { 32 entity.State = EntityState.Detached; 33 } 34 Console.WriteLine(context.Set<Note>().First().Name); 35 } 36 } 37 }
分析
第一个输出之所以不是期望的结果是因为EntityFramework内置了主键映射模式,内存状态还是处于非法状态,虽然First会导致一次数据库往返。
第二个输出之所以正确是因为清空了主键映射,这样会导致重新用数据库的内容填充主键映射。
结论
出现异常最好终止线程或程序的执行,上边这种BUG是因为使用了一种异常反模式:“把异常作为正常的逻辑处理流程”。
备注
这个错误我犯过,后来的朋友也犯过。