C# 使用事务或 SaveChanges(false) 和 AcceptAllChanges()?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/815586/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 02:17:47  来源:igfitidea点击:

Using Transactions or SaveChanges(false) and AcceptAllChanges()?

c#entity-frameworktransactions

提问by mark smith

I have been investigating transactions and it appears that they take care of themselves in EF as long as I pass falseto SaveChanges()and then call AcceptAllChanges()if there are no errors:

我一直在调查交易,似乎他们在 EF 中照顾自己,只要我传递falseSaveChanges()然后调用,AcceptAllChanges()如果没有错误:

SaveChanges(false);
// ...
AcceptAllChanges();

What if something goes bad? don't I have to rollback or, as soon as my method goes out of scope, is the transaction ended?

如果出现问题怎么办?我不必回滚,或者一旦我的方法超出范围,事务就结束了吗?

What happens to any indentiy columns that were assigned half way through the transaction? I presume if somebody else added a record after mine before mine went bad then this means there will be a missing Identity value.

在事务中途分配的任何 indenty 列会发生什么情况?我认为如果在我的记录变坏之前有人在我的记录之后添加了一条记录,那么这意味着将缺少 Identity 值。

Is there any reason to use the standard TransactionScopeclass in my code?

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

采纳答案by Alex James

With the Entity Framework most of the time SaveChanges()is sufficient. This creates a transaction, or enlists in any ambient transaction, and does all the necessary work in that transaction.

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

Sometimes though the SaveChanges(false) + AcceptAllChanges()pairing is useful.

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

The most useful place for this is in situations where you want to do a distributed transaction across two different Contexts.

最有用的地方是在您想要跨两个不同的上下文进行分布式事务的情况下。

I.e. something like this (bad):

即这样的事情(坏):

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();
}

If context1.SaveChanges()succeeds but context2.SaveChanges()fails the whole distributed transaction is aborted. But unfortunately the Entity Framework has already discarded the changes on context1, so you can't replay or effectively log the failure.

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

But if you change your code to look like this:

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

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();

}

While the call to SaveChanges(false)sends the necessary commands to the database, the context itself is not changed, so you can do it again if necessary, or you can interrogate the ObjectStateManagerif you want.

虽然调用SaveChanges(false)将必要的命令发送到数据库,但上下文本身并没有改变,因此您可以在必要时再次执行此操作,或者您可以根据需要询问ObjectStateManager

This means if the transaction actually throws an exception you can compensate, by either re-trying or logging state of each contexts ObjectStateManagersomewhere.

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

See myblog postfor more.

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

回答by user3885816

If you are using EF6 (Entity Framework 6+), this has changed for database calls to SQL.
See: http://msdn.microsoft.com/en-us/data/dn456843.aspx

如果您使用的是 EF6(实体框架 6+),这已更改为对 SQL 的数据库调用。
请参阅:http: //msdn.microsoft.com/en-us/data/dn456843.aspx

use context.Database.BeginTransaction.

使用 context.Database.BeginTransaction。

From MSDN:

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

回答by eMeL

Because some database can throw an exception at dbContextTransaction.Commit() so better this:

因为某些数据库可以在 dbContextTransaction.Commit() 处抛出异常所以更好:

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(false); 

      dbContextTransaction.Commit(); 

      context.AcceptAllChanges();
    } 
    catch (Exception) 
    { 
      dbContextTransaction.Rollback(); 
    } 
  } 
}