我一直在调查交易,只要我将false传递给SaveChanges(),然后在没有错误的情况下致电AcceptAllChanges(),它们似乎就会在EF中照顾自己:

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


如果出现问题怎么办?

在事务中途分配的所有intentiy列会发生什么情况?我想如果有人在我的坏事发生之前在我的事后添加了一条记录,那么这意味着将丢失一个Identity值。

是否有任何理由在我的代码中使用标准TransactionScope类?

评论

这帮助我了解了为什么SaveChanges(fase); ... AcceptAllChanges();首先是一种模式。请注意,博客作者是如何回答上述问题的,答案是另一个问题。全部都聚在一起。

#1 楼

使用实体框架,大多数时候SaveChanges()就足够了。这将创建一个事务,或加入任何环境事务,并在该事务中完成所有必要的工作。因为这是您要在两个不同的上下文中进行分布式事务的情况。

即像这样(不好):

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


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

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

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

}


尽管对context1的调用将必要的命令发送到数据库,但上下文本身未更改,因此您可以在必要时再次进行操作,也可以根据需要查询SaveChanges(false)

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

评论


太好了,谢谢...因此,如果出现故障,我不必回滚吗? SaveChanges,将其标记为已保存,但是直到我接受allallchanges才真正提交。.但是如果出现问题..我将需要回滚,这样我的对象才能返回到正确的状态吗?

–马克·史密斯
09年5月10日在19:32

@Mark:如果通过“回滚”表示将对象恢复为它们在数据库中的状态,则不会,因为您将丢失所有用户对对象的更改,因此您不想这样做。 SaveChanges(false)对数据库进行实际更新,而AcceptAllChanges()告诉EF:“好的,您可以忘记需要保存哪些内容,因为它们已成功保存。”如果SaveChanges(false)失败,则将永远不会调用AcceptAllChanges(),并且EF仍将您的对象视为具有已更改的属性,需要将其保存回数据库。

– BlueRaja-Danny Pflughoeft
2010年3月29日15:22



您能建议使用Code First做到这一点吗? SaveChanges或AcceptAllChanges方法没有参数

–克尔斯滕·格里德(Kirsten Greed)
13年2月17日在5:44

我在这里问了一个关于将这种技术与Code First结合使用的问题

–克尔斯滕·格里德(Kirsten Greed)
13年2月18日在21:47



在EF 6.1中不再可能。您知道现在需要进行哪些调整才能工作吗?

– Alex Dresko
2014年7月24日在22:44

#2 楼

如果您使用的是EF6(Entity Framework 6+),则此更改已更改为对SQL的数据库调用。
请参阅:http://msdn.microsoft.com/zh-cn/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
        } 
    } 
} 



评论


在事务上使用“使用”时,不需要使用回滚的try-catch。

–罗伯特
2014年11月6日上午10:41

我正在捕获这样的异常。它导致数据库操作静默失败。由于SO的性质,有人可能会举这个例子并在生产应用程序中使用它。

– B2K
15年7月27日在14:32

@ B2K:很好,但是此代码是从链接的Microsoft文章中复制的。我希望没有人在生产中使用他们的代码:)

– J布赖恩·普莱斯(J Bryan Price)
2015年8月1日在20:18

@Robert根据MSDN文章Rollback()是必需的。他们故意为TransactionScope示例省略了“回滚”命令。我添加了@ B2K;到MSDN代码段,并清楚地表明它不是MSDN文章中的原始内容。

–托德
16-4-3在12:41

(如果正确)这可能会解决问题:听起来像EF + MSSQL不需要回滚,但是EF +其他SQL提供程序可能需要。由于EF应该与它正在与哪个数据库无关,所以在与MySql或没有自动行为的情况下调用Rollback()。

–讨厌
16 Jun 13'20:52



#3 楼

因为某些数据库可以在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(); 
    } 
  } 
} 


评论


我正在捕获这样的异常。它导致数据库操作静默失败。由于SO的性质,有人可能会举这个例子并在生产应用程序中使用它。

– B2K
15年7月27日在14:32

这与其他引用归因于其引用的MSDN页面的答案是否基本相同?我看到的唯一区别是,您将false传递给context.SaveChanges();,并另外调用context.AcceptAllChanges();。

–李慧霞
2015年12月7日12:12



@ B2K不需要回滚-如果事务不起作用,则不会提交任何内容。同样,显式调用回滚可能会失败-在此处查看我的答案stackoverflow.com/questions/41385740/…

–肯
18-10-10在0:42

回滚不是我反对的目标。答案的作者更新了他们的代码以重新抛出异常,从而解决了我反对的问题。

– B2K
18-10-11在3:54

抱歉,我用手机发表了评论。 Todd重新引发异常,eMeL不会。捕获中应该有一些东西可以通知开发人员或用户导致回滚的问题。那可能是写入日志文件,重新抛出异常或向用户返回消息。

– B2K
18-10-12在15:06