• 第五节:EF Core中的三类事务(SaveChanges、DbContextTransaction、TransactionScope)


    一. 说明

     EF版本的事务介绍详见:

      第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope)。

      本节主要介绍EF Core下的三种事务的用法和各自的使用场景,其中SaveChanges和DBContextTransaction事务与EF版本的基本一致,在该章节中补充一些新的使用场景和配置方式,TransactionScope环境事务与EF 版本的有着本质的区别,它目前不支持分布式数据库事务。

      后面章节将继续介绍事务的基础概念、事务的隔离级别和带来的各种问题。

    二. 默认事务(SaveChanges)

    (1).默认情况下,如果数据库提供程序支持事务,单个 SaveChanges() 调用中的所有变更都会在一个事务中被提交。如果其中任何一个变更失败了, 那么事务就会回滚,没有任何变更会被应用到数据库。这意味着 SaveChanges() 能够确保要么成功保存,要么在发生错误时不对数据库做任何修改。

    (2).关闭默认事务:context.Database.AutoTransactionsEnabled = false; 如:Test3()方法,第一条数据保存成功,第二条失败。

     代码分享:

     1         /// <summary>
     2         /// 全部成功
     3         /// </summary>
     4         public static void Test1()
     5         {
     6             using (EFDB01Context db = new EFDB01Context())
     7             {
     8                 db.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
     9                 db.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员2", addTime = DateTime.Now });
    10                 db.SaveChanges();
    11             }
    12         }
    13 
    14         /// <summary>
    15         /// 全部失败
    16         /// </summary>
    17         public static void Test2()
    18         {
    19             using (EFDB01Context db = new EFDB01Context())
    20             {
    21                 try
    22                 {
    23                     db.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    24                     db.T_RoleInfor.Add(new T_RoleInfor() { id = 123, roleName = "管理员2", addTime = DateTime.Now });
    25                     db.SaveChanges();
    26                 }
    27                 catch (Exception)
    28                 {
    29                     Console.WriteLine("出错了,两条数据都没有执行成功");
    30                 }
    31             }
    32         }
    33 
    34         /// <summary>
    35         /// 第一条成功,第二条失败
    36         /// </summary>
    37         public static void Test3()
    38         {
    39             using (EFDB01Context db = new EFDB01Context())
    40             {
    41                 try
    42                 {
    43                     //关闭SaveChanges的默认事务
    44                     db.Database.AutoTransactionsEnabled = false;
    45 
    46                     db.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    47                     db.T_RoleInfor.Add(new T_RoleInfor() { id = 123, roleName = "管理员2", addTime = DateTime.Now });
    48 
    49                     //db.T_UserInfor.Add(new T_UserInfor() { id = Guid.NewGuid().ToString("N"), userName = "管理员1", addTime = DateTime.Now });
    50                     //db.T_UserInfor.Add(new T_UserInfor() { id = Guid.NewGuid().ToString("N")+"123", userName = "管理员2", addTime = DateTime.Now });
    51 
    52                     db.SaveChanges();
    53                 }
    54                 catch (Exception)
    55                 {
    56                     Console.WriteLine("出错了,第一条数据插入成功了");
    57                 }
    58             }
    59         }

    三. DbContextTransaction

    1. 使用方式

      BeginTransaction开启事务、Commit提交事务、Rollback回滚事务、Dispose销毁,如果用Using包裹的话,不再需要手动Rollback,走完Using会自动回滚。如果不用Using包裹事务,就需要在Catch中手动RollBack回滚,并且最好最后手动的Dispose一下。(如SameDbContext文件夹中的Test1和Test2方法)

    2. 使用场景

     A. 同一个上下文多个SaveChanges的方法(如:自增主键后续要用到,如Test2方法)、SaveChanges和EF调用SQL语句混用(如Test2方法)

     1        /// <summary>
     2         /// 三条添加语句共享同一个事务,最后使用 transaction.Commit() 统一提交,三条全部执行成功,则影响到数据库,
     3         /// 如果任何一个命令失败,则在事务被回收(Dispose)时会自动回滚,对数据库无影响。 
     4         /// </summary>
     5         public static void Test1()
     6         {
     7             using (EFDB01Context db = new EFDB01Context())
     8             {
     9                 using (var transaction = db.Database.BeginTransaction())
    10                 {
    11                     try
    12                     {
    13                         db.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    14                         db.SaveChanges();
    15 
    16                         db.T_RoleInfor.Add(new T_RoleInfor() { id = 111, roleName = "管理员2", addTime = DateTime.Now });  //报错
    17                         db.SaveChanges();
    18 
    19                         string sql1 = @"insert into T_RoleInfor (roleName,roleDescription,addTime) values (@roleName,@roleDescription,@addTime)";
    20                         SqlParameter[] pars1 ={
    21                                                  new SqlParameter("@roleName","管理员3"),
    22                                                  new SqlParameter("@roleDescription","txt11"),
    23                                                  new SqlParameter("@addTime",DateTime.Now)
    24                                             };
    25                         db.Database.ExecuteSqlCommand(sql1, pars1);
    26                         transaction.Commit();
    27 
    28                         Console.WriteLine("成功了");
    29                     }
    30                     catch (Exception)
    31                     {
    32                         Console.WriteLine("失败了");
    33                     }
    34                 }
    35             }
    36         }
    37 
    38         /// <summary>
    39         /// 如果不用Using包裹事务,就需要在Catch中手动RollBack回滚
    40         /// </summary>
    41         public static void Test2()
    42         {
    43             using (EFDB01Context db = new EFDB01Context())
    44             {
    45                 var transaction = db.Database.BeginTransaction();
    46                 try
    47                 {
    48                     var d1 = new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now };
    49                     db.T_RoleInfor.Add(d1);
    50                     db.SaveChanges();
    51 
    52                     db.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员2"+d1.id, addTime = DateTime.Now });
    53                     db.SaveChanges();
    54 
    55                     string sql1 = @"insert into T_RoleInfor (roleName,roleDescription,addTime) values (@roleName,@roleDescription,@addTime)";
    56                     SqlParameter[] pars1 ={
    57                                                  new SqlParameter("@roleName","管理员3"),
    58                                                  new SqlParameter("@roleDescription","txt11"),
    59                                                  new SqlParameter("@addTime",DateTime.Now)
    60                                             };
    61                     db.Database.ExecuteSqlCommand(sql1, pars1);
    62                     transaction.Commit();
    63 
    64                     Console.WriteLine("成功了");
    65 
    66                 }
    67                 catch (Exception)
    68                 {
    69                     transaction.Rollback();
    70                     Console.WriteLine("失败了");
    71                 }
    72                 finally
    73                 {
    74                     transaction.Dispose();
    75                 }
    76 
    77             }
    78         }

     B. 同一个数据库多个上下文但“同一个连接”的事务。其中一个上下文开启事务,另外上下文通过UseTransaction方法来实现共享事务。

     情况①:

      EFDB01Context直接在OnConfiguring中写死连接字符串,多次new上下文,如Test1方法,则是多个连接,不能共享事务。

     1  /// <summary>
     2         /// 情况一:在OnConfiguring中书写连接字符串,创建两个上下文,相当于两个连接,两个连接之间不能通过使用UseTransaction,建立事务连接。
     3         /// 会报下面的错。
     4         /// The specified transaction is not associated with the current connection. Only transactions associated with the current connection may be used.
     5         /// </summary>
     6         public static void Test1()
     7         {
     8             using (EFDB01Context context1 = new EFDB01Context())
     9             {
    10                 using (var transaction = context1.Database.BeginTransaction())
    11                 {
    12                     try
    13                     {
    14                         context1.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    15                         context1.SaveChanges();
    16 
    17                         using (EFDB01Context context2 = new EFDB01Context())
    18                         {
    19                             context2.Database.UseTransaction(transaction.GetDbTransaction());
    20 
    21                             context1.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    22                             context1.SaveChanges();
    23                         }
    24 
    25                         //统一提交
    26                         transaction.Commit();
    27                         Console.WriteLine("成功了");
    28                     }
    29                     catch (Exception ex)
    30                     {
    31                         Console.WriteLine(ex.Message);
    32                     }
    33                 }
    34             }
    35 
    36 
    37         }
    View Code

     情况②:

      EFDB01Context2通过 public EFDB01Context2(DbContextOptions<EFDB01Context2> options) : base(options)这种形式的构造函数,然后new的时候 统一传入: new  DbContextOptionsBuilder<EFDB01Context2>().UseSqlServer(new SqlConnection(connectionString)).Options;,从而共享连接,如Test2方法。

     1         /// <summary>
     2         /// 情况二:通过父类构造函数
     3         /// </summary>
     4         public static void Test2()
     5         {
     6             var connectionString = "Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
     7             //将连接拿出来,传到多个上下文中,这样是共享同一个连接
     8             var option = new DbContextOptionsBuilder<EFDB01Context2>().UseSqlServer(new SqlConnection(connectionString)).Options;
     9 
    10             using (var context1 = new EFDB01Context2(option))
    11             {
    12                 using (var transaction = context1.Database.BeginTransaction())
    13                 {
    14                     try
    15                     {
    16                         context1.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    17                         context1.SaveChanges();
    18 
    19                         using (var context2 = new EFDB01Context2(option))
    20                         {
    21                             context2.Database.UseTransaction(transaction.GetDbTransaction());
    22 
    23                             context1.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    24                             context1.SaveChanges();
    25                         }
    26 
    27                         //统一提交
    28                         transaction.Commit();
    29                         Console.WriteLine("成功了");
    30                     }
    31                     catch (Exception ex)
    32                     {
    33                         Console.WriteLine(ex.Message);
    34                     }
    35                 }
    36             }
    37         }

     情况③:

     EFDB01Context3通过 传入SqlConnection来实现共享连接,如Test3方法。

     1         public static void Test3()
     2         {
     3             var connectionString = "Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
     4             //将连接拿出来,传到多个上下文中,这样是共享同一个连接
     5             var connection = new SqlConnection(connectionString);
     6 
     7             using (var context1 = new EFDB01Context3(connection))
     8             {
     9                 using (var transaction = context1.Database.BeginTransaction())
    10                 {
    11                     try
    12                     {
    13                         context1.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    14                         context1.SaveChanges();
    15 
    16                         using (var context2 = new EFDB01Context3(connection))
    17                         {
    18                             context2.Database.UseTransaction(transaction.GetDbTransaction());
    19 
    20                             context1.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    21                             context1.SaveChanges();
    22                         }
    23 
    24                         //统一提交
    25                         transaction.Commit();
    26                         Console.WriteLine("成功了");
    27                     }
    28                     catch (Exception ex)
    29                     {
    30                         Console.WriteLine(ex.Message);
    31                     }
    32                 }
    33             }
    34         }

     C. 多种数据库技术同一个数据库的事务

      如ADO.Net和EF共同使用,利用方法 “UseTransaction”共享同一个事务,共同提交。 如:Test1方法

     1         /// <summary>
     2         /// ADO.Net 和 EF混用,多种数据库技术访问同一个数据库
     3         /// </summary>
     4         public static void Test1()
     5         {
     6             var conStr = @"Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
     7             using (var connection=new SqlConnection(conStr))
     8             {
     9                 connection.Open();
    10                 using (var transaction=connection.BeginTransaction())
    11                 {
    12                     try
    13                     {
    14                         //ADO.Net
    15                         var command = connection.CreateCommand();
    16                         command.Transaction = transaction;
    17                         command.CommandText = "DELETE FROM T_RoleInfor";
    18                         command.ExecuteNonQuery();
    19 
    20                         //EF
    21                         var options = new DbContextOptionsBuilder<EFDB01Context>().UseSqlServer(connection).Options;
    22                         using (var context = new EFDB01Context(options))
    23                         {
    24                             context.Database.UseTransaction(transaction);
    25                             context.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    26                             context.SaveChanges();
    27                         }
    28                         //综合提交
    29                         transaction.Commit();
    30 
    31                         Console.WriteLine("成功了");
    32 
    33                     }
    34                     catch (Exception ex)
    35                     {
    36                         Console.WriteLine(ex.Message);
    37                     }
    38                 }
    39                
    40             }
    41         }

    四. 环境事务

    1.使用方式

      new TransactionScope创建事务、Complete提交事务、 Transaction.Current.Rollback();回滚事务、Dispose销毁对象。如果用Using包裹的话,不再需要手动Rollback,走完Using会自动回滚。如果不用Using包裹事务,就需要在Catch中手动RollBack回滚,并且最好最后手动的Dispose一下。

    2.用途

     A. 同一个上下文的事务。(多个SaveChanges(自增主键后续用到的情况)、SaveChanges和EF调用SQL语句混用)(如Test1方法)

     1  /// <summary>
     2         /// A. 同一个上下文的事务。(多个SaveChanges(自增主键后续用到的情况)、SaveChanges和EF调用SQL语句混用)
     3         /// </summary>
     4         public static void Test1()
     5         {
     6             using (EFDB01Context1 db = new EFDB01Context1())
     7             {
     8                 using (var scope = new TransactionScope(/*TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }*/))
     9                 {
    10                     try
    11                     {
    12                         var data1 = new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now };
    13                         db.T_RoleInfor.Add(data1);
    14                         db.SaveChanges();
    15 
    16                         db.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员2" + data1.id, addTime = DateTime.Now });  //报错
    17                         db.SaveChanges();
    18 
    19                         string sql1 = @"insert into T_RoleInfor (roleName,roleDescription,addTime) values (@roleName,@roleDescription,@addTime)";
    20                         SqlParameter[] pars1 ={
    21                                                  new SqlParameter("@roleName","管理员3"),
    22                                                  new SqlParameter("@roleDescription","txt11"),
    23                                                  new SqlParameter("@addTime",DateTime.Now)
    24                                             };
    25                         db.Database.ExecuteSqlCommand(sql1, pars1);
    26                         scope.Complete();
    27 
    28 
    29                         Console.WriteLine("成功了");
    30                     }
    31                     catch (Exception)
    32                     {
    33                         Console.WriteLine("失败了");
    34                     }
    35                 }
    36             }
    37         }
    View Code

     B. 多种数据库技术访问同一个数据库的事务 (如Test2方法)

     1         /// <summary>
     2         /// B. 多种数据库技术访问同一个数据库的事务
     3         /// </summary>
     4         public static void Test2()
     5         {
     6             var conStr = @"Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
     7             using (var connection = new SqlConnection(conStr))
     8             {
     9                 connection.Open();
    10                 using (var scope = new TransactionScope())
    11                 {
    12                     try
    13                     {
    14                         //ADO.Net
    15                         var command = connection.CreateCommand();
    16                         command.CommandText = "DELETE FROM T_RoleInfor";
    17                         command.ExecuteNonQuery();
    18 
    19                         //EF
    20                         using (var context = new EFDB01Context1())
    21                         {
    22                             context.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    23                             context.SaveChanges();
    24                         }
    25                         //综合提交
    26                         scope.Complete();
    27 
    28                         Console.WriteLine("成功了");
    29 
    30                     }
    31                     catch (Exception ex)
    32                     {
    33                         Console.WriteLine(ex.Message);
    34                     }
    35                 }
    36             }
    37         }
    View Code

     C. 同一个数据库多个不同的上下文是支持的(如Test3方法)

    上下文代码:

     1   public partial class EFDB01Context1 : DbContext
     2     {
     3         public virtual DbSet<T_RoleInfor> T_RoleInfor { get; set; }
     4         public virtual DbSet<T_UserInfor> T_UserInfor { get; set; }
     5 
     6         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     7         {
     8             optionsBuilder.UseSqlServer("Server=localhost;Database=EFDB01;User ID=sa;Password=123456;");
     9         }
    10 
    11         protected override void OnModelCreating(ModelBuilder modelBuilder)
    12         {
    13             modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
    14 
    15             modelBuilder.Entity<T_RoleInfor>(entity =>
    16             {
    17                 entity.Property(e => e.roleDescription).IsUnicode(false);
    18 
    19                 entity.Property(e => e.roleName).IsUnicode(false);
    20             });
    21 
    22             modelBuilder.Entity<T_UserInfor>(entity =>
    23             {
    24                 entity.Property(e => e.id)
    25                     .IsUnicode(false)
    26                     .ValueGeneratedNever();
    27 
    28                 entity.Property(e => e.userName).IsUnicode(false);
    29 
    30                 entity.Property(e => e.userSex).IsUnicode(false);
    31             });
    32         }
    33     }
    EFDB01Context1
     1   public partial class EFDB01Context2 : DbContext
     2     {
     3         public virtual DbSet<T_RoleInfor> T_RoleInfor { get; set; }
     4         public virtual DbSet<T_UserInfor> T_UserInfor { get; set; }
     5 
     6         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     7         {
     8             optionsBuilder.UseSqlServer("Server=localhost;Database=EFDB01;User ID=sa;Password=123456;");
     9         }
    10 
    11         protected override void OnModelCreating(ModelBuilder modelBuilder)
    12         {
    13             modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
    14 
    15             modelBuilder.Entity<T_RoleInfor>(entity =>
    16             {
    17                 entity.Property(e => e.roleDescription).IsUnicode(false);
    18 
    19                 entity.Property(e => e.roleName).IsUnicode(false);
    20             });
    21 
    22             modelBuilder.Entity<T_UserInfor>(entity =>
    23             {
    24                 entity.Property(e => e.id)
    25                     .IsUnicode(false)
    26                     .ValueGeneratedNever();
    27 
    28                 entity.Property(e => e.userName).IsUnicode(false);
    29 
    30                 entity.Property(e => e.userSex).IsUnicode(false);
    31             });
    32         }
    33     }
    EFDB01Context2

    运行代码:

     1        /// <summary>
     2         ///C. 同一个数据库两个不同上下文是支持的
     3         /// </summary>
     4         public static void Test3()
     5         {
     6             using (var scope = new TransactionScope())
     7             {
     8                 try
     9                 {
    10                     using (var context = new EFDB01Context1())
    11                     {
    12                         context.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    13                         context.SaveChanges();
    14                     }
    15                     using (var context = new EFDB01Context2())
    16                     {
    17                         context.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    18                         context.SaveChanges();
    19                     }
    20 
    21                     //综合提交
    22                     scope.Complete();
    23 
    24                     Console.WriteLine("成功了");
    25                 }
    26                 catch (Exception ex)
    27                 {
    28                     Console.WriteLine(ex.Message);
    29                 }
    30             }
    31         }

     D. 不同数据库的上下文是不支持的,(如Test4方法,开启msdtc服务的步骤: cmd命令→ net start msdtc ,然后发现报错:This platform does not support distributed transactions.说明目前Core平台下不支持分布式事务)

    上下文代码:

     1  public partial class dbCore1Context : DbContext
     2     {
     3         public dbCore1Context()
     4         {
     5         }
     6 
     7         public dbCore1Context(DbContextOptions<dbCore1Context> options)
     8             : base(options)
     9         {
    10         }
    11 
    12         public virtual DbSet<UserInfors> UserInfors { get; set; }
    13 
    14         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    15         {
    16             optionsBuilder.UseSqlServer("Server=localhost;Database=dbCore1;User ID=sa;Password=123456;");
    17 
    18         }
    19 
    20         protected override void OnModelCreating(ModelBuilder modelBuilder)
    21         {
    22             modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
    23 
    24             modelBuilder.Entity<UserInfors>(entity =>
    25             {
    26                 entity.Property(e => e.id).ValueGeneratedNever();
    27             });
    28         }
    29     }
    dbCore1Context
     1   public partial class EFDB01Context1 : DbContext
     2     {
     3         public virtual DbSet<T_RoleInfor> T_RoleInfor { get; set; }
     4         public virtual DbSet<T_UserInfor> T_UserInfor { get; set; }
     5 
     6         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     7         {
     8             optionsBuilder.UseSqlServer("Server=localhost;Database=EFDB01;User ID=sa;Password=123456;");
     9         }
    10 
    11         protected override void OnModelCreating(ModelBuilder modelBuilder)
    12         {
    13             modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
    14 
    15             modelBuilder.Entity<T_RoleInfor>(entity =>
    16             {
    17                 entity.Property(e => e.roleDescription).IsUnicode(false);
    18 
    19                 entity.Property(e => e.roleName).IsUnicode(false);
    20             });
    21 
    22             modelBuilder.Entity<T_UserInfor>(entity =>
    23             {
    24                 entity.Property(e => e.id)
    25                     .IsUnicode(false)
    26                     .ValueGeneratedNever();
    27 
    28                 entity.Property(e => e.userName).IsUnicode(false);
    29 
    30                 entity.Property(e => e.userSex).IsUnicode(false);
    31             });
    32         }
    33     }
    EFDB01Context1

    运行代码:

     1         /// <summary>
     2         ///D. 不同数据库之间的事务
     3         /// </summary>
     4         public static void Test4()
     5         {
     6             using (var scope = new TransactionScope())
     7             {
     8                 try
     9                 {
    10                     using (var context = new EFDB01Context1())
    11                     {
    12                         context.T_RoleInfor.Add(new T_RoleInfor() { roleName = "管理员1", addTime = DateTime.Now });
    13                         context.SaveChanges();
    14                     }
    15                     using (var context = new dbCore1Context())
    16                     {
    17                         context.UserInfors.Add(new UserInfors() { id = Guid.NewGuid().ToString("N"), userName = "管理员1", userSex = "" });
    18                         context.SaveChanges();
    19                     }
    20 
    21                     //综合提交
    22                     scope.Complete();
    23 
    24                     Console.WriteLine("成功了");
    25                 }
    26                 catch (Exception ex)
    27                 {
    28                     Console.WriteLine(ex.Message);
    29                 }
    30             }
    31         }

     注:EF Core中的 System.Transactions 实现将不包括对分布式事务的支持,因此不能使用 TransactionScope 或 CommittableTransaction 来跨多个资源管理器协调事务。主要分布式事务需要依赖于 Windows 系统的 MSDTC 服务,但.NET Core要实现跨平台,基于跨平台的分布式事务没有统一的标准,后续版希望改进。

     

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    坚持--从今天开始
    51系列单片机的精确延时的解释(文章如有问题之处,请劳烦指正,谢谢!) 可以看看采纳下。
    利用宏定义实现C++程序在Unix和Win32环境下的通用性
    [转]浅谈C++指针直接调用类成员函数
    类间调用inline函数的效率
    C++ inline函数与编译器设置
    GNU的makefile文件编写说明
    Windows Live Writer 2012 Test
    测试Windows Live Writer
    Mathematica学习笔记2
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/11387935.html
Copyright © 2020-2023  润新知