使用Transactions或SaveChanges(false)和AcceptAllChanges()?

mar*_*ith 340 c# entity-framework transactions

我一直在研究交易似乎因为我通过他们照顾自己的EF只要falseSaveChanges(),然后调用AcceptAllChanges(),如果没有错误:

SaveChanges(false);
// ...
AcceptAllChanges();
Run Code Online (Sandbox Code Playgroud)

如果事情变坏怎么办?我不必回滚,或者一旦我的方法超出范围,交易结束了吗?

在事务中途分配的任何indentiy列会发生什么?我认为如果其他人在我的事情发生之前添加了一条记录,那么这意味着会有一个缺失的身份值.

有没有理由TransactionScope在我的代码中使用标准类?

Ale*_*mes 444

使用实体框架大部分时间SaveChanges()都足够了.这会创建一个事务,或在任何环境事务中登记,并在该事务中完成所有必要的工作.

有时虽然SaveChanges(false) + AcceptAllChanges()配对很有用.

对此最有用的地方是您希望跨两个不同的上下文执行分布式事务.

就是这样(坏):

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save and discard changes
    context1.SaveChanges();

    //Save and discard changes
    context2.SaveChanges();

    //if we get here things are looking good.
    scope.Complete();
}
Run Code Online (Sandbox Code Playgroud)

如果 context1.SaveChanges()成功但context2.SaveChanges()失败则中止整个分布式事务.但不幸的是,实体框架已经放弃了更改context1,因此您无法重播或有效地记录失败.

但是,如果您将代码更改为如下所示:

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save Changes but don't discard yet
    context1.SaveChanges(false);

    //Save Changes but don't discard yet
    context2.SaveChanges(false);

    //if we get here things are looking good.
    scope.Complete();
    context1.AcceptAllChanges();
    context2.AcceptAllChanges();

}
Run Code Online (Sandbox Code Playgroud)

在调用SaveChanges(false)将必要的命令发送到数据库时,上下文本身不会更改,因此您可以在必要时再次执行此操作,或者您可以根据需要查询ObjectStateManager.

这意味着如果事务实际抛出异常,您可以通过在ObjectStateManager某处重新尝试或记录每个上下文的状态来进行补偿.

有关更多信息,请参阅我的 博文

  • @Mark:如果通过"回滚"意味着将对象恢复到它们在数据库中的状态,那么不,你不想这样做,因为你丢失了所有用户对对象的更改.`SaveChanges(false)`对数据库进行实际更新,而`AcceptAllChanges()`告诉EF,"好吧,你可以忘记需要保存的东西,因为它们已经成功保存了." 如果`SaveChanges(false)`失败,`AcceptAllChanges()`将永远不会被调用,EF仍会认为你的对象具有已更改的属性并需要保存回数据库. (32认同)
  • 在EF 6.1中不再可能.你知道现在需要做哪些调整吗? (12认同)
  • 太好了,谢谢...所以,如果出现故障,我不必回滚吗?SaveChanges,将其标记为已保存,但是直到我接受allchanges才真正提交。.但是如果出现问题..我将需要回滚,这样我的对象才能恢复到正确的状态吗? (2认同)
  • 我已经问过一个关于在Code First中使用这种技术的问题[这里](http://stackoverflow.com/questions/14944802/doing-transactions-with-entity-framework-code-first) (2认同)

小智 108

如果您使用的是EF6(实体框架6+),那么对于SQL的数据库调用,这已经发生了变化.
请参阅:http://msdn.microsoft.com/en-us/data/dn456843.aspx

使用context.Database.BeginTransaction.

来自MSDN:

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
        try 
        { 
            context.Database.ExecuteSqlCommand( 
                @"UPDATE Blogs SET Rating = 5" + 
                    " WHERE Name LIKE '%Entity Framework%'" 
                ); 

            var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
            foreach (var post in query) 
            { 
                post.Title += "[Cool Blog]"; 
            } 

            context.SaveChanges(); 

            dbContextTransaction.Commit(); 
        } 
        catch (Exception) 
        { 
            dbContextTransaction.Rollback(); //Required according to MSDN article 
            throw; //Not in MSDN article, but recommended so the exception still bubbles up
        } 
    } 
} 
Run Code Online (Sandbox Code Playgroud)

  • 在交易中使用"使用"时,不需要使用带有roolback的try-catch. (48认同)
  • 我正在采取异常来捕获这样的异常.它会导致数据库操作无提示失败.由于SO的性质,有人可能会采用此示例并在生产应用程序中使用它. (11认同)
  • @Robert根据MSDN文章Rollback()是必要的.他们故意省略了TransactionScope示例的Rollback命令.@ B2K我在`throw;`中加入了MSDN片段并清楚地表明它不是MSDN文章中的原文. (6认同)
  • [(如果正确)这可能会清除:](http://stackoverflow.com/a/28915506)听起来像EF + MSSQL不需要回滚,但EF +其他SQL提供程序可能.由于EF应该与它正在讨论的数据库无关,因此在调用MySql或没有自动行为的情况下调用`Rollback()`. (6认同)
  • @ B2K:好点,但这段代码是从[链接](https://msdn.microsoft.com/en-us/data/dn456843.aspx)微软文章中复制而来的.我希望没有人在生产中使用_their_代码:) (3认同)
  • 我注意到,如果您在两个或多个数据库上下文中工作,这根本不适用.为此你仍然需要TransactionScope (2认同)
  • 事务要么提交要么回滚。不需要显式调用。此外,如果存在严重的 SQL 错误,则显式调用回滚将失败。https://github.com/aspnet/EntityFramework.Docs/issues/327 /sf/ask/2897001831/#52731028 (2认同)