实体框架没有拿起交易范围

时间:2014-04-17 09:16:55

标签: c# entity-framework azure-sql-database transactionscope

我有几乎标准的EF 6.1'在事务范围中包装的数据库'代码中创建对象。无论出于何种原因,在事务失败(完成)后数据仍然存在于db中。

代码:

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 
    }
}

在ResendActivation(...)中抛出SaveChanges()异常后,不应保存更改。但是记录留在数据库中。

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

4 个答案:

答案 0 :(得分:1)

如果我正确地调用它而不是使用事务范围,请尝试使用自己的db实例中的事务,db.Database.BeginTransaction()。

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

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

答案 1 :(得分:1)

经过更多的调查后,发现了一些东西 - 可能是Entity Framework升级或数据库更新过程已经放了

Enlist=false;

进入数据库连接字符串。这有效地阻止了EF获取交易范围。

所以解决方案是将其设置为true,或者删除它,我认为默认情况下它是真的

答案 2 :(得分:0)

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

在给定的代码中,我们正在调用一个负责创建域用户的方法,即CreateDomainUser,也许还有自己的上下文对象。因此这个问题。

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

当我们将ADO.NET调用与Entity框架混合时,通常需要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

答案 3 :(得分:0)

您应该能够将TransactionScope与EF一起使用,我知道我们的项目会这样做。但是,我认为你希望在事务范围内实例化EF上下文 - 也就是说,我认为你需要交换最外面/前两个using语句,正如@rahuls的回答所示。

即使它以另一种方式工作......如果你有一个需要更新多个表的服务/应用/业务层方法,并且你希望这些更新是原子的,你需要这样做。因此,为了保持一致性(以及您自己的理智),我建议首先考虑事务范围,然后是第二个上下文。