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

时间:2009-05-02 20:50:03

标签: c# entity-framework transactions

我一直在调查交易,只要我将false传递给SaveChanges(),然后在没有错误的情况下调用AcceptAllChanges(),他们似乎会在EF中照顾好自己:< / p>

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

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

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

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

3 个答案:

答案 0 :(得分:438)

使用实体框架大部分时间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();
}

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

}

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

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

有关详情,请参阅my blog post

答案 1 :(得分:106)

如果您使用的是EF6(Entity Framework 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
        } 
    } 
} 

答案 2 :(得分:-5)

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