我在单元测试中使用交易来回滚更改.单元测试使用dbcontext,我正在测试的服务使用自己的.它们都包裹在一个事务中,一个dbcontext在另一个事务的块中.事情是,当内部dbcontext保存他的更改时,外部dbcontext不可见(我不认为这是因为其他dbcontext可能已经加载了该对象).这是例子:
[TestMethod] public void EditDepartmentTest() { using (TransactionScope transaction = new TransactionScope()) { using (MyDbContext db = new MyDbContext()) { //Arrange int departmentId = (from d in db.Departments where d.Name == "Dep1" select d.Id).Single(); string newName = "newName",newCode = "newCode"; //Act IDepartmentService service = new DepartmentService(); service.EditDepartment(departmentId,newName,newCode); //Assert Department department = db.Departments.Find(departmentId); Assert.AreEqual(newName,department.Name,"Unexpected department name!"); //Exception is thrown because department.Name is "Dep1" instead of "newName" Assert.AreEqual(newCode,department.Code,"Unexpected department code!"); } } }
服务:
public class DepartmentService : IDepartmentService { public void EditDepartment(int DepartmentId,string Name,string Code) { using (MyDbContext db = new MyDbContext ()) { Department department = db.Departments.Find(DepartmentId); department.Name = Name; department.Code = Code; db.SaveChanges(); } } }
但是,如果在调用该服务之前关闭外部dbcontext并为该断言打开一个新的dbcontext,则一切正常:
[TestMethod] public void EditDepartmentTest() { using (TransactionScope transaction = new TransactionScope()) { int departmentId=0; string newName = "newName",newCode = "newCode"; using (MyDbContext db = new MyDbContext()) { //Arrange departmentId = (from d in db.Departments where d.Name == "Dep1" select d.Id).Single(); } //Act IDepartmentService service = new DepartmentService(); service.EditDepartment(departmentId,newCode); using (MyDbContext db = new MyDbContext()) { //Assert Department department = db.Departments.Find(departmentId); Assert.AreEqual(newName,"Unexpected department name!"); Assert.AreEqual(newCode,"Unexpected department code!"); } } }
所以基本上我有一个这个问题的解决方案(在编写这个问题时想到),但是我仍然想知道当dbcontexts嵌套时,为什么不可能在事务中访问未提交的数据.
可能是因为使用(dbcontext)就像一个事务本身?如果是这样,我仍然不明白这个问题,因为我在内部dbcontext上调用.SaveChanges().
解决方法
在第一种情况下,您正在嵌套DbContexts.打开与数据库的连接.当您在使用块中调用服务方法时,将在另一个已经打开的TransactionScope内打开一个新连接.这将导致您的事务升级为
distributed transaction,部分提交的数据(服务中的DbContext.SaveChanges调用的结果)不可用于外部连接.还要注意,分布式事务的速度要慢得多,因此这样做会降低性能的副作用.
在第二种情况下,当您打开和关闭三个连接时,在事务中只能同时打开一个连接.由于这些连接共享相同的连接字符串,事务将不会自动升级到分布式连接,因此事务中的每个后续连接都可以访问由先前连接执行的更改.
您可以尝试将Enlist = false参数添加到连接字符串.这将禁用在分布式事务中自动登记,导致在第一种情况下引发异常.如果您使用的是sql Server 2008及更高版本,则第二种情况将保持正常工作,因为该事务不会被提升. (Prior versions of SQL Server will still promote the transaction in this scenario.)
你也可能会发现有用的this great answer相当类似的问题.