2014-04-17 131 views
1

我在包装在事务范围中的数据库代码中拥有非常标准的EF 6.1'创建对象。无论出于何种原因,数据在事务失败(完成)后仍然保留在数据库中。实体框架没有拿起交易范围

代码:

using (var db = this.Container.Resolve<SharedDataEntities>()) // << new instance of DbContext 
{ 
    using (TransactionScope ts = new TransactionScope()) 
    { 
     SubscriptionTypes st = this.SubscriptionType.Value; 

     if (st == SubscriptionTypes.Lite && this.ProTrial) 
      st = SubscriptionTypes.ProTrial; 

     Domain domain = new Domain() 
     { 
      Address = this.Address.Trim(), 
      AdminUserId = (Guid)user.ProviderUserKey, 
      AdminUserName = user.UserName, 
      Description = this.Description.TrimSafe(), 
      DomainKey = Guid.NewGuid(), 
      Enabled = !masterSettings.DomainEnableControlled.Value, 
      Name = this.Name.Trim(), 
      SubscriptionType = (int)st, 
      Timezone = this.Timezone, 
      Website = this.Website.TrimSafe(), 
      IsPrivate = this.IsPrivate 
     }; 

     foreach (var countryId in this.Countries) 
     { 
      domain.DomainCountries.Add(new DomainCountry() { CountryId = countryId, Domain = domain }); 
     } 

     db.Domains.Add(domain); 
     db.SaveChanges(); // << This is the Saving that should not be commited until we call 'ts.Complete()' 

     this.ResendActivation(domain); // << This is where the Exception occurs 

     using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.Suppress)) 
     { 
      this.DomainMembership.CreateDomainUser(domain.Id, (Guid)user.ProviderUserKey, user.UserName, DomainRoles.DomainSuperAdmin | DomainRoles.Driver); 
      ts2.Complete(); 
     } 
     this.Enabled = domain.Enabled; 
     ts.Complete(); // << Transaction commit never happens 
    } 
} 

后的SaveChanges()抛出异常里面ResendActivation(...)这样的变化不应该被保存。但是记录保留在数据库中。

没有其他TransactionScope包装我粘贴的代码,它是由MVC Action调用触发的。

+0

尝试创建你的第一个事务范围,就像这个'新的TransactionScope(TransactionScopeOption.Required,new TransactionOptions(){IsolationLevel = IsolationLevel.ReadCommitted})' – Andrew

回答

1

尝试使用db实例中的事务it self,db.Database.BeginTransaction()如果我正确地记住它而不是使用事务作用域。

using (var ts = db.Database.BeginTransaction()) 
{ 
.. 
} 

假设db是您的实体框架上下文。

+1

这可能工作,但它并没有解决我发布的代码。我已经发布的代码应该工作的方式是 – user1275154

+0

注意,'context.Database.BeginTransaction()'只在EF6 +可用。 – David

1

更多的调查后,原来的东西 - 也许实体框架升级或数据库更新过程中已经把

Enlist=false; 

到数据库连接字符串。这有效地阻止了EF拾取事务范围。

因此,解决办法是将其设置为true,或将其删除,我认为在默认情况下它是默认支持事务也是如此

0

Context类。但是对于每个新的上下文类实例,都会创建一个新的事务。这个新的事务是一个嵌套的事务,一旦关联的上下文类的SaveChanges()被调用,它就会被提交。

在给定的代码中,我们似乎调用了一个负责创建域用户的方法,即CreateDomainUser,并且可能拥有自己的上下文对象。因此这个问题。

如果是这种情况(即这种方法有自己的上下文),我们甚至可能在这里甚至不需要TransactionScope。我们可以简单地将相同的上下文(我们在此调用之前使用的)传递给正在创建域用户的函数。然后我们可以检查两个操作的结果,如果它们成功,我们只需调用SaveChanges()方法。

当我们将ADO.NET调用与实体框架混合时,通常需要TransactionScope。我们也可以在上下文中使用它,但由于上下文类已经有一个事务并且我们可以简单地使用相同的上下文来管理事务,所以它会过度。如果在上面的代码中出现这种情况,那么解决问题的技巧就是让上下文类知道你想用它来处理你自己的事务。由于事务与范围内的连接对象相关联,所以我们需要使用与事务范围关联的连接的上下文类。此外,我们需要让上下文类知道它不能拥有连接,因为它由调用代码拥有。所以我们可以这样做:

using (var scope = new TransactionScope(TransactionScopeOption.Required)) 
{ 
    using (var conn = new SqlConnection("...")) 
    { 
     conn.Open(); 

     var sqlCommand = new SqlCommand(); 
     sqlCommand.Connection = conn; 
     sqlCommand.CommandText = 
      @"UPDATE Blogs SET Rating = 5" + 
       " WHERE Name LIKE '%Entity Framework%'"; 
     sqlCommand.ExecuteNonQuery(); 

     using (var context = 
      new BloggingContext(conn, contextOwnsConnection: false)) 
     { 
      var query = context.Posts.Where(p => p.Blog.Rating > 5); 
      foreach (var post in query) 
      { 
       post.Title += "[Cool Blog]"; 
      } 
      context.SaveChanges(); 
     } 
    } 

    scope.Complete(); 
} 

参见:http://msdn.microsoft.com/en-us/data/dn456843.aspx

0

您应该能够使用TransactionScope与EF,我知道我们的项目做。不过,我想你想的事务范围内实例化的EF背景下 - 也就是说,我相信你需要交换的最外层/前两个using语句,从@rahuls答案建议。

即使它的工作原理其他方式...如果你有一个需要更新几个表的业务/应用/业务层的方法,并且希望这些更新是原子,你需要做这种方式。所以为了一致性(和你自己的理智),我会首先推荐交易范围,其次是背景。