zoukankan      html  css  js  c++  java
  • 第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope)

    一. 什么是事务

       我们通俗的理解事务就是一系列操作要么全部成功、要么全部失败(不可能存在部分成功,部分失败的情况)。

       举一个事务在我们日常生活中的经典例子:两张银行卡(甲、乙),甲向乙转钱,整个过程需要执行两个操作,甲账户钱减少,乙账户钱增加,这是转账成功的情况;转账失败时候,二者均不执行,甲乙账户钱都不变。

    1. SQLServer中的事务

      SQLServer中的事务具体封装方式有多种,但无论是哪种,都需要依赖于这三句代码:begin transaction 、commit transaction、rollback transaction。 

       具体怎么使用,详见数据库事务章节:去复习

    2. EF中的事务

       EF中的事务主要分为三类,分别是SaveChanges、DbContextTransaction、TransactionScope。

       需要掌握的是这三种事务各自的使用方式和适用场景。

    二. SaveChanges

       SaveChanges是EF中最常见的事务了,在前面章节,多次提到,SaveChanges一次性将本地缓存中所有的状态变化一次性提交到数据库,这就是一个事务,要么统一成功,要么统一回滚。

       应用场景:该种事务模式在实际开发中非常常用,在处理模块业务同时需要对一张表(或多张表)进行一系列增删改操作,但这些操作都是要么都成功,要么都失败,这种情况下可以在流程的最后 调用SaveChanges,一次性保存本地属性状态的全部变化。

      该模式作用于一个DBContext,即一个数据库的EF的上下文,不能控制多个数据库。

        比如:注册场景,可能需要向用户表、日志表、账户表等多张表中插入数据,其中一张表插入失败,所有的都需要回滚,这种情况,就可以在该业务的最后,统一调用SaveChanges一次性的事务提交。

    1. 事务成功

     1      private static void TestSaveChanges()
     2         {
     3             using (DbContext db = new CodeFirstModel())
     4             {
     5                 //增加
     6                 TestInfor t1 = new TestInfor()
     7                 {
     8                     id = Guid.NewGuid().ToString("N"),
     9                     txt1 = "txt1",
    10                     txt2 = "txt2"
    11                 };
    12                 db.Set<TestInfor>().Add(t1);
    13                 //删除
    14                 TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "1").FirstOrDefault();
    15                 if (t2 != null)
    16                 {
    17                     db.Set<TestInfor>().Remove(t2);
    18                 }
    19                 //修改
    20                 TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault();
    21                 t3.txt2 = "我是李马茹23";
    22 
    23                 //SaveChanges事务提交
    24                 int n = db.SaveChanges();
    25                 Console.WriteLine("数据作用条数:" + n);
    26             }
    27         }    

    2. 事务回滚

     1       private static void TestSaveChangesFailure()
     2         {
     3             using (DbContext db = new CodeFirstModel())
     4             {
     5                 //增加
     6                 TestInfor t1 = new TestInfor()
     7                 {
     8                     id = Guid.NewGuid().ToString("N"),
     9                     txt1 = "txt1",
    10                     txt2 = "txt2"
    11                 };
    12                 db.Set<TestInfor>().Add(t1);
    13                 //删除
    14                 TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "1").FirstOrDefault();
    15                 if (t2 != null)
    16                 {
    17                     db.Set<TestInfor>().Remove(t2);
    18                 }
    19                 //修改
    20                 TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault();
    21                 t3.txt2 = "我是李马茹23";
    22                 //增加(制造错误)
    23                 TestInfor t4 = new TestInfor()
    24                 {
    25                     id = Guid.NewGuid().ToString("N") + "123",
    26                     txt1 = "txt1",
    27                     txt2 = "txt2"
    28                 };
    29                 db.Set<TestInfor>().Add(t4);
    30 
    31                 //SaveChanges事务提交
    32                 int n = db.SaveChanges();
    33                 Console.WriteLine("数据作用条数:" + n);
    34             }
    35         }

    三. DbContextTransaction事务

      该事务为EF6新增的事务,通常用于手动接管事务,某些操作是一个事务,某些操作是另外一个事务。

      使用场景:EF调用SQL语句的时候使用该事务、 多个SaveChanges的情况(解决自增ID另一个业务中使用的场景)。

      核心代码:BeginTransaction、Commit、Rollback、Dispose. 如果放到using块中,就不需要手动Dispose了。

      该种事务与数据库中的transaction事务原理一致,在EF中,由于每次调用 db.Database.ExecuteSqlCommand(sql1, pars1)的时候,即刻执行了该SQL语句,所以要把他放到一个大的事务中,整体提交、回滚.(EF6新增的)

    1. 事务成功

     1      private static void TestDbContextTransaction()
     2         {
     3             using (DbContext db = new CodeFirstModel())
     4             {
     5                 DbContextTransaction trans = null;
     6                 try
     7                 {
     8                     //开启事务
     9                     trans = db.Database.BeginTransaction();
    10                     //增加
    11                     string sql1 = @"insert into TestInfor  values(@id,@txt1,@txt2)";
    12                     SqlParameter[] pars1 ={
    13                                                  new SqlParameter("@id",Guid.NewGuid().ToString("N")),
    14                                                  new SqlParameter("@txt1","txt11"),
    15                                                  new SqlParameter("@txt2","txt22")
    16                                             };
    17                     db.Database.ExecuteSqlCommand(sql1, pars1);
    18                     //删除
    19                     string sql2 = @"delete from TestInfor where id=@id";
    20                     SqlParameter[] pars2 ={
    21                                                  new SqlParameter("@id","22")
    22                                             };
    23                     db.Database.ExecuteSqlCommand(sql2, pars2);
    24                     //修改
    25                     string sql3 = @"update TestInfor set txt1=@txt1 where id=@id";
    26                     SqlParameter[] pars3 ={
    27                                                  new SqlParameter("@id","3"),
    28                                                  new SqlParameter("@txt1","二狗子")
    29                                             };
    30                     db.Database.ExecuteSqlCommand(sql3, pars3);
    31 
    32                     //提交事务
    33                     trans.Commit();
    34                     Console.WriteLine("事务成功了");
    35                 }
    36                 catch (Exception ex)
    37                 {
    38                     Console.WriteLine(ex.Message);
    39                     trans.Rollback();
    40 
    41                 }
    42                 finally
    43                 {
    44                     trans.Dispose();     //也可以把该事务写到using块中,让其自己托管,就不需要手动释放了
    45                 }
    46             }
    47         }

    2. 事务回滚

     1      private static void TestDbContextTransactionFailure()
     2         {
     3             using (DbContext db = new CodeFirstModel())
     4             {
     5                 DbContextTransaction trans = null;
     6                 try
     7                 {
     8                     //开启事务
     9                     trans = db.Database.BeginTransaction();
    10                     //增加
    11                     string sql1 = @"insert into TestInfor  values(@id,@txt1,@txt2)";
    12                     SqlParameter[] pars1 ={
    13                                                  new SqlParameter("@id",Guid.NewGuid().ToString("N")),
    14                                                  new SqlParameter("@txt1","txt11"),
    15                                                  new SqlParameter("@txt2","txt22")
    16                                             };
    17                     db.Database.ExecuteSqlCommand(sql1, pars1);
    18                     //删除
    19                     string sql2 = @"delete from TestInfor where id=@id";
    20                     SqlParameter[] pars2 ={
    21                                                  new SqlParameter("@id","22")
    22                                             };
    23                     db.Database.ExecuteSqlCommand(sql2, pars2);
    24                     //修改
    25                     string sql3 = @"update TestInfor set txt1=@txt1 where id=@id";
    26                     SqlParameter[] pars3 ={
    27                                                  new SqlParameter("@id","3"),
    28                                                  new SqlParameter("@txt1","二狗子222")
    29                                             };
    30                     db.Database.ExecuteSqlCommand(sql3, pars3);
    31 
    32 
    33                     //增加(制造错误)
    34                     string sql4 = @"insert into TestInfor  values(@id,@txt1,@txt2)";
    35                     SqlParameter[] pars4 ={
    36                                                  new SqlParameter("@id",Guid.NewGuid().ToString("N")+"123"),
    37                                                  new SqlParameter("@txt1","txt11"),
    38                                                  new SqlParameter("@txt2","txt22")
    39                                             };
    40                     db.Database.ExecuteSqlCommand(sql4, pars4);
    41 
    42                     //提交事务
    43                     trans.Commit();
    44                     Console.WriteLine("事务成功了");
    45                 }
    46                 catch (Exception ex)
    47                 {
    48                     Console.WriteLine(ex.Message);
    49                     trans.Rollback();
    50 
    51                 }
    52                 finally
    53                 {
    54                     trans.Dispose();
    55                 }
    56             }
    57         }

    3. 自DbContextTransaction事务测试多个SaveChanges的情况统一回滚

     1   private static void DbContextTransSaveChanges()
     2         {
     3             using (DbContext db = new CodeFirstModel())
     4             {
     5                 //自动脱管,不需要手动释放
     6                 using (DbContextTransaction trans = db.Database.BeginTransaction())
     7                 {
     8                     try
     9                     {
    10                         TestInfor t1 = new TestInfor()
    11                         {
    12                             id = Guid.NewGuid().ToString("N"),
    13                             txt1 = "111111111",
    14                             txt2 = "222222222222"
    15                         };
    16                         db.Entry(t1).State = EntityState.Added;
    17                         db.SaveChanges();
    18 
    19                         //人为制造失败情况
    20                         TestInfor t2 = new TestInfor()
    21                         {
    22                             id = Guid.NewGuid().ToString("N") + "123",
    23                             txt1 = "111111111",
    24                             txt2 = "222222222222"
    25                         };
    26                         db.Entry(t2).State = EntityState.Added;
    27                         db.SaveChanges();
    28 
    29                         trans.Commit();
    30                     }
    31                     catch (Exception)
    32                     {
    33                         trans.Rollback();
    34                     }
    35 
    36                 }
    37             }
    38         }

    四. TransactionScope事务

    1. 该事务用来处理多个SaveChanges的事务(特殊情况的业务)或者多个DBContext(每个DBContext是一个实例,代表不同的数据库连接).

    2. 核心代码:(一个Complete函数走天下,异常的话,自动回滚 ,也可以结合try-catch Transaction.Current.Rollback();实现回滚)

             需要引入程序集:using System.Transactions;

    3. 适用场景:

      ①该种事务适用于多数据库连接的情况

      特别注意:如果使用该事务来处理多个数据库(多个DBContex)时,必须手动开启msdtc服务,这样才可以将多个DB的SaveChange给放到一个事务中,如果失败, 则多个数据库的数据统一回滚.

         开启msdtc服务的步骤: cmd命令→net start msdtc

        ②主键id自增的情况,同一个业务线中,需要拿到新增加的数据的主键id,进行操作。

          ③多线程带锁的情况,同一条业务线前半部分必须先SaveChanges,才能保证数据准确性(测试简单版本,实际的业务场景待补充!!!)

    4. 主键自增被使用的情况的解决方案

     1     private static void Test3()
     2         {
     3             using (DbContext db = new CodeFirstModel())
     4             {
     5                 using (TransactionScope trans = new TransactionScope())
     6                 {
     7                     try
     8                     {
     9                         TestInfor2 t1 = new TestInfor2()
    10                         {
    11                             txt11 = Guid.NewGuid().ToString("N"),
    12                             txt22 = Guid.NewGuid().ToString("N")
    13                         };
    14                         db.Set<TestInfor2>().Add(t1);
    15                         //如果这里不写SaveChanges的话,t1.id永远为0
    16                         db.SaveChanges();
    17 
    18 
    19                         TestInfor2 t2 = new TestInfor2()
    20                         {
    21                             txt11 = (t1.id + 1).ToString(),
    22                             txt22 = Guid.NewGuid().ToString("N")
    23                         };
    24                         db.Entry(t2).State = EntityState.Added;
    25                         db.SaveChanges();
    26 
    27                         //事务统一提交(若失败则统一回滚)
    28                         trans.Complete();
    29                         Console.WriteLine("自增提交成功");
    30                     }
    31                     catch (Exception)
    32                     {
    33                         Transaction.Current.Rollback();
    34                     }
    35                 }    
    36             }
    37         }

    5. 多个DBContext、多个数据库的情况。

     1  private static void TransactionScopeTwoDB()
     2         {
     3             using (TransactionScope trans = new TransactionScope())
     4             {
     5                 try
     6                 {
     7                     DbContext db1 = new CodeFirstModel();
     8                     DbContext db2 = new CodeFirstModel2();
     9                     //数据库1
    10                     TestInfor t1 = new TestInfor()
    11                     {
    12                         id = Guid.NewGuid().ToString("N"),
    13                         txt1 = "111111111",
    14                         txt2 = "222222222222"
    15                     };
    16                     db1.Entry(t1).State = EntityState.Added;
    17                     db1.SaveChanges();
    18 
    19                     //数据库2
    20                     TestOne t2 = new TestOne()
    21                     {
    22                         id = Guid.NewGuid().ToString("N"),    //可以在此处手动制造个错误,来测试多数据库的回滚问题
    23                         t1 = "222",
    24                         t2 = "2222"
    25                     };
    26                     db2.Entry(t2).State = EntityState.Added;
    27                     db2.SaveChanges();
    28 
    29                     trans.Complete();
    30                     Console.WriteLine("多个数据库添加成功");
    31                 }
    32                 catch (Exception)
    33                 {
    34                     Transaction.Current.Rollback();
    35                 }
    36 
    37             }
    38         }
  • 相关阅读:
    正负样本不平衡的处理
    基于贝叶斯优化的超参数搜索
    Axure RP的基础使用
    keil:C语言里面调用汇编程序
    keil的使用:新建Project
    汇编入门:循环语句的汇编实现
    转gif图
    LeetCode题解 #5 Longest Palindromic Substring
    Android 从本地图库或拍照后裁剪图片并设置头像
    Android使用简单的Service
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/7748221.html
Copyright © 2011-2022 走看看