• EntityFramework 事务处理


    默认情况下,当EF调用SaveChanges()时,会把生成的所有SQL命令“包”到一个“事务(transaction)”中,只要有一个数据更新操作失败,整个事务将回滚。

    在多数情况下,如果你总在数据更新操作代码中使用一个而不是多个DbContext对象,并且只是在最后调用一次SaveChanges(),那么EF的默认事务处理机制己经够用了,无需做额外的事情。

    然而,如果出现以下的情形,你就必须显式地处理事务了。

    第一种情况:你需要分阶段地保存数据,因而需要多次调用SaveChanges()或者执行修改数据库的SQL命令。

    请看以下示例代码:

    using (var context = new MyDbContext())
    
    {
        try
         {
            Person3 p = context.People3.First();
    
            p.Name ="newName" + (new Random().Next(1, 100));
    
            context.SaveChanges();
    
            context.Database.ExecuteSqlCommand("update Person3 setDescription={0} where Person3Id={1}",
    
                                "DescriptionModified at " + DateTime.Now.ToShortTimeString(),
    
                                p.Person3Id);
            p.age *= 2;
            context.SaveChanges();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
         }
    }

    上述代码中,调用两次SaveChanges(),还有一次执行Update命令。

    如果在最后一次SaveChanges()中出现异常,虽然最后一次没成功,但你会发现前两次数据己经保存!这就带来了数据不一致的问题。

    对于这种场景,你需要显式地编写事务代码了(注:以下代码适用于EF6):

    using (var context = new MyDbContext())
     {
        using (var transaction =context.Database.BeginTransaction())
        {
            try
            {
                  ……
    
                  context.SaveChanges();
    
                  context.Database.ExecuteSqlCommand("……);
    
                  ……
    
                  context.SaveChanges();
                  transaction.Commit();
    
             }
            catch (Exception e)
            {
                   Console.WriteLine(e.Message);
                   transaction.Rollback();
            }
        }
    }
    

    特别要注意一定要调用commit(),我测试发现,只要不Commit,即使没有异常发生,事务仍将回滚,数据库中的数据不会更新。

    第2种情况,你需要使用多个DbContext保存数据

    以下是处理这种场景的典型代码:

    static void TestTransactionScope2()
    
    {
               using (TransactionScope scope = new TransactionScope())
               {
                    String connStr = ……;
                    using (var conn = newSqlConnection(connStr))
                    {
                        try
                        {
                            conn.Open();
                            using (var context1 =new MyDbContext(conn, contextOwnsConnection: false))
                            {
                                 ……
                                 context1.SaveChanges();
                            }
                          using (var context2 =new MyDbContext(conn, contextOwnsConnection: false))
                           {
                               context2.Database.ExecuteSqlCommand(……);
                               context2.SaveChanges();
                           }
                           using (var context3 =new MyDbContext2(conn, contextOwnsConnection: false))
                           {
                                ……
                                context3.SaveChanges();
                           }
                          scope.Complete();
                     }
                    catch (Exception e)
                    {
                           Console.WriteLine(e.ToString());
                    }
                    finally
                    {
                            conn.Close();
                     }
                }
          }
    }
    

    上述代码中有几个关键点:

    (1)在构造DbContext对象时,需要把一个己打开的数据库连接对象传给它,并且需要指定EF在DbContext对象销毁时不关闭数据库连接。

    为实现此目的,你的DbContext对象应该类似于是这样的,提供两个重载的构造函数:

    public class MyDbContext2 : DbContext
        {
    
           public MyDbContext2(DbConnection conn, boolcontextOwnsConnection):base(conn,contextOwnsConnection)
           {
    
           }
    
           public MyDbContext2():base()
           {
    
           }
           public DbSet<OtherEntity> OtherEntities { get; set; }
    
           ……
    }
    

    注意在代码结束时关闭连接。

    (2)如果不Commit,则所有数据将不会保存。

    (3)你的计算机需要启动MSDTC(分布式交易协调器),请先在控制面板中打开Distributed Transaction Coordinator服务,否则上述代码将在运行时抛出MSDTC服务不可用的异常。

    很明显,当事务需要使用多个不同类型的DbContext对象时,Windows需要启动MSDTC,这会对性能有所影响,因此在开发中应该尽量避免这种情况,如无必要,不要在单个事务中使用多个不同种类的DbContext对象。

    转自:http://blog.csdn.net/bitfan/article/details/14231561

  • 相关阅读:
    092 Reverse Linked List II 反转链表 II
    091 Decode Ways 解码方法
    090 Subsets II 子集 II
    089 Gray Code 格雷编码
    088 Merge Sorted Array 合并两个有序数组
    bzoj1218: [HNOI2003]激光炸弹
    bzoj1293: [SCOI2009]生日礼物
    bzoj3438: 小M的作物
    bzoj2565: 最长双回文串
    bzoj3172: [Tjoi2013]单词
  • 原文地址:https://www.cnblogs.com/hycms/p/5384560.html
Copyright © 2020-2023  润新知