首页 文章

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

提问于
浏览
327

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

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

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

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

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

3 回答

  • 434

    使用实体框架大部分时间 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 .

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

    有关更多信息,请参见my blog post .

  • -4

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

    使用context.Database.BeginTransaction .

    来自MSDN:

    using(var context = new BloggingContext())
    {
    使用(var dbContextTransaction = context.Database.BeginTransaction())
    {
    尝试
    {
    context.Database.ExecuteSqlCommand(
    @“更新博客SET评级= 5”
    “WHERE Name LIKE'%Entity Framework%'”
    );

    var query = context.Posts.Where(p => p.Blog.Rating> = 5);
    foreach(查询中的var post)

    context.SaveChanges();

    dbContextTransaction.Commit();
    }
    catch(例外)
    {
    dbContextTransaction.Rollback(); //根据MSDN文章要求
    扔; //不在MSDN文章中,但建议如此异常仍然存在
    }
    }
    }

  • 101

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

相关问题