当前位置:网站首页>Three types of transactions in EF core (saveChanges, dbcontexttransaction, transactionscope)

Three types of transactions in EF core (saveChanges, dbcontexttransaction, transactionscope)

2022-06-23 23:51:00 I left my life to work

Default transaction (SaveChanges)

(1). By default , If the database provider supports transactions , Single SaveChanges() All changes in the invocation are committed in a transaction . If any of these changes fail , Then the transaction rolls back , No changes will be applied to the database . It means SaveChanges() Be able to ensure that it is either saved successfully , Or do not make any changes to the database when an error occurs .

(2). Close the default transaction :context.Database.AutoTransactionsEnabled = false; Such as :Test3() Method , First data saved successfully , The second failure .

/// <summary>
        ///  All success 
        /// </summary>
        public static void Test1()
        {
    
            using (EFDB01Context db = new EFDB01Context())
            {
    
                db.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                db.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 2", addTime = DateTime.Now });
                db.SaveChanges();
            }
        }

        /// <summary>
        ///  All failed 
        /// </summary>
        public static void Test2()
        {
    
            using (EFDB01Context db = new EFDB01Context())
            {
    
                try
                {
    
                    db.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                    db.T_RoleInfor.Add(new T_RoleInfor() {
     id = 123, roleName = " Administrators 2", addTime = DateTime.Now });
                    db.SaveChanges();
                }
                catch (Exception)
                {
    
                    Console.WriteLine(" Something went wrong , Neither data was successfully executed ");
                }
            }
        }

        /// <summary>
        ///  Article 1 success , The second failure 
        /// </summary>
        public static void Test3()
        {
    
            using (EFDB01Context db = new EFDB01Context())
            {
    
                try
                {
    
                    // close SaveChanges Default transaction for 
                    db.Database.AutoTransactionsEnabled = false;

                    db.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                    db.T_RoleInfor.Add(new T_RoleInfor() {
     id = 123, roleName = " Administrators 2", addTime = DateTime.Now });

                    //db.T_UserInfor.Add(new T_UserInfor() { id = Guid.NewGuid().ToString("N"), userName = " Administrators 1", addTime = DateTime.Now });
                    //db.T_UserInfor.Add(new T_UserInfor() { id = Guid.NewGuid().ToString("N")+"123", userName = " Administrators 2", addTime = DateTime.Now });

                    db.SaveChanges();
                }
                catch (Exception)
                {
    
                    Console.WriteLine(" Something went wrong , The first piece of data was inserted successfully ");
                }
            }
        }

DbContextTransaction

1. Usage mode

BeginTransaction Open transaction 、Commit Commit transaction 、Rollback Roll back the transaction 、Dispose The destruction , If you use Using If the package , No more manual Rollback, Walk the Using Will automatically roll back . If not Using Parcel transaction , It needs to be in Catch Medium manual RollBack Roll back , And it's best to do it manually Dispose once .( Such as SameDbContext In folder Test1 and Test2 Method )

2. Use scenarios

A. Multiple in the same context SaveChanges Methods ( Such as : The auto increment primary key will be used later , Such as Test2 Method )、SaveChanges and EF call SQL Statement mixing ( Such as Test2 Method )

/// <summary>
        ///  Three add statements share the same transaction , Finally using  transaction.Commit()  Unified submission , All three items were successfully implemented , Affect the database ,
        ///  If any command fails , The transaction is recycled (Dispose) Will be rolled back automatically , No impact on the database .
        /// </summary>
        public static void Test1()
        {
    
            using (EFDB01Context db = new EFDB01Context())
            {
    
                using (var transaction = db.Database.BeginTransaction())
                {
    
                    try
                    {
    
                        db.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                        db.SaveChanges();

                        db.T_RoleInfor.Add(new T_RoleInfor() {
     id = 111, roleName = " Administrators 2", addTime = DateTime.Now });  // Report errors 
                        db.SaveChanges();

                        string sql1 = @"insert into T_RoleInfor (roleName,roleDescription,addTime) values (@roleName,@roleDescription,@addTime)";
                        SqlParameter[] pars1 ={
    
                                                 new SqlParameter("@roleName"," Administrators 3"),
                                                 new SqlParameter("@roleDescription","txt11"),
                                                 new SqlParameter("@addTime",DateTime.Now)
                                            };
                        db.Database.ExecuteSqlCommand(sql1, pars1);
                        transaction.Commit();

                        Console.WriteLine(" succeed ");
                    }
                    catch (Exception)
                    {
    
                        Console.WriteLine(" failed ");
                    }
                }
            }
        }

        /// <summary>
        ///  If not Using Parcel transaction , It needs to be in Catch Medium manual RollBack Roll back 
        /// </summary>
        public static void Test2()
        {
    
            using (EFDB01Context db = new EFDB01Context())
            {
    
                var transaction = db.Database.BeginTransaction();
                try
                {
    
                    var d1 = new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now };
                    db.T_RoleInfor.Add(d1);
                    db.SaveChanges();

                    db.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 2"+d1.id, addTime = DateTime.Now });
                    db.SaveChanges();

                    string sql1 = @"insert into T_RoleInfor (roleName,roleDescription,addTime) values (@roleName,@roleDescription,@addTime)";
                    SqlParameter[] pars1 ={
    
                                                 new SqlParameter("@roleName"," Administrators 3"),
                                                 new SqlParameter("@roleDescription","txt11"),
                                                 new SqlParameter("@addTime",DateTime.Now)
                                            };
                    db.Database.ExecuteSqlCommand(sql1, pars1);
                    transaction.Commit();

                    Console.WriteLine(" succeed ");

                }
                catch (Exception)
                {
    
                    transaction.Rollback();
                    Console.WriteLine(" failed ");
                }
                finally
                {
    
                    transaction.Dispose();
                }

            }
        }

B. The same database has multiple contexts but “ The same connection ” The business of . One of the contexts starts the transaction , In addition, the context passes through UseTransaction Method to implement shared transactions .

situation ①:

EFDB01Context Directly in OnConfiguring Write a dead connection string in , many times new Context , Such as Test1 Method , Multiple connections , Cannot share transactions .
   Insert picture description here

/// <summary>
        ///  Situation 1 : stay OnConfiguring Write the connection string in , Create two contexts , Equivalent to two connections , Two connections cannot be connected by using UseTransaction, Establish a transaction connection .
        ///  I'll report the following error .
        /// The specified transaction is not associated with the current connection. Only transactions associated with the current connection may be used.
        /// </summary>
        public static void Test1()
        {
    
            using (EFDB01Context context1 = new EFDB01Context())
            {
    
                using (var transaction = context1.Database.BeginTransaction())
                {
    
                    try
                    {
    
                        context1.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                        context1.SaveChanges();

                        using (EFDB01Context context2 = new EFDB01Context())
                        {
    
                            context2.Database.UseTransaction(transaction.GetDbTransaction());

                            context1.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                            context1.SaveChanges();
                        }

                        // Unified submission 
                        transaction.Commit();
                        Console.WriteLine(" succeed ");
                    }
                    catch (Exception ex)
                    {
    
                        Console.WriteLine(ex.Message);
                    }
                }
            }


        }

situation ②:

EFDB01Context2 adopt public EFDB01Context2(DbContextOptions options) : base(options) This form of constructor , then new When Unified incoming : new DbContextOptionsBuilder().UseSqlServer(new SqlConnection(connectionString)).Options;, To share connections , Such as Test2 Method .

 Insert picture description here

/// <summary>
        ///  Situation two : Through the parent class constructor 
        /// </summary>
        public static void Test2()
        {
    
            var connectionString = "Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
            // Take out the connection , To multiple contexts , This is sharing the same connection 
            var option = new DbContextOptionsBuilder<EFDB01Context2>().UseSqlServer(new SqlConnection(connectionString)).Options;

            using (var context1 = new EFDB01Context2(option))
            {
    
                using (var transaction = context1.Database.BeginTransaction())
                {
    
                    try
                    {
    
                        context1.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                        context1.SaveChanges();

                        using (var context2 = new EFDB01Context2(option))
                        {
    
                            context2.Database.UseTransaction(transaction.GetDbTransaction());

                            context1.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                            context1.SaveChanges();
                        }

                        // Unified submission 
                        transaction.Commit();
                        Console.WriteLine(" succeed ");
                    }
                    catch (Exception ex)
                    {
    
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }

situation ③:

EFDB01Context3 adopt Pass in SqlConnection To realize shared connection , Such as Test3 Method .
  Insert picture description here

public static void Test3()
        {
    
            var connectionString = "Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
            // Take out the connection , To multiple contexts , This is sharing the same connection 
            var connection = new SqlConnection(connectionString);

            using (var context1 = new EFDB01Context3(connection))
            {
    
                using (var transaction = context1.Database.BeginTransaction())
                {
    
                    try
                    {
    
                        context1.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                        context1.SaveChanges();

                        using (var context2 = new EFDB01Context3(connection))
                        {
    
                            context2.Database.UseTransaction(transaction.GetDbTransaction());

                            context1.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                            context1.SaveChanges();
                        }

                        // Unified submission 
                        transaction.Commit();
                        Console.WriteLine(" succeed ");
                    }
                    catch (Exception ex)
                    {
    
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }

C. Multiple database technologies transactions in the same database

Such as ADO.Net and EF The common use , Utilization method “UseTransaction” Share the same transaction , Jointly submit . Such as :Test1 Method

/// <summary>
        /// ADO.Net  and  EF A mixture of , Multiple database technologies access the same database 
        /// </summary>
        public static void Test1()
        {
    
            var conStr = @"Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
            using (var connection=new SqlConnection(conStr))
            {
    
                connection.Open();
                using (var transaction=connection.BeginTransaction())
                {
    
                    try
                    {
    
                        //ADO.Net
                        var command = connection.CreateCommand();
                        command.Transaction = transaction;
                        command.CommandText = "DELETE FROM T_RoleInfor";
                        command.ExecuteNonQuery();

                        //EF
                        var options = new DbContextOptionsBuilder<EFDB01Context>().UseSqlServer(connection).Options;
                        using (var context = new EFDB01Context(options))
                        {
    
                            context.Database.UseTransaction(transaction);
                            context.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                            context.SaveChanges();
                        }
                        // Comprehensive submission 
                        transaction.Commit();

                        Console.WriteLine(" succeed ");

                    }
                    catch (Exception ex)
                    {
    
                        Console.WriteLine(ex.Message);
                    }
                }

            }
        }

Environmental Affairs (TransactionScope)

1. Usage mode

new TransactionScope Create transaction 、Complete Commit transaction 、 Transaction.Current.Rollback(); Roll back the transaction 、Dispose Destroy object . If you use Using If the package , No more manual Rollback, Walk the Using Will automatically roll back . If not Using Parcel transaction , It needs to be in Catch Medium manual RollBack Roll back , And it's best to do it manually Dispose once .

2. purpose

A. Transactions in the same context .( Multiple SaveChanges( Subsequent use of auto increment primary key )、SaveChanges and EF call SQL Statement mixing )( Such as Test1 Method )

/// <summary>
        /// A.  Transactions in the same context .( Multiple SaveChanges( Subsequent use of auto increment primary key )、SaveChanges and EF call SQL Statement mixing )
        /// </summary>
        public static void Test1()
        {
    
            using (EFDB01Context1 db = new EFDB01Context1())
            {
    
                using (var scope = new TransactionScope(/*TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }*/))
                {
    
                    try
                    {
    
                        var data1 = new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now };
                        db.T_RoleInfor.Add(data1);
                        db.SaveChanges();

                        db.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 2" + data1.id, addTime = DateTime.Now });  // Report errors 
                        db.SaveChanges();

                        string sql1 = @"insert into T_RoleInfor (roleName,roleDescription,addTime) values (@roleName,@roleDescription,@addTime)";
                        SqlParameter[] pars1 ={
    
                                                 new SqlParameter("@roleName"," Administrators 3"),
                                                 new SqlParameter("@roleDescription","txt11"),
                                                 new SqlParameter("@addTime",DateTime.Now)
                                            };
                        db.Database.ExecuteSqlCommand(sql1, pars1);
                        scope.Complete();


                        Console.WriteLine(" succeed ");
                    }
                    catch (Exception)
                    {
    
                        Console.WriteLine(" failed ");
                    }
                }
            }
        }

B. Multiple database technologies access transactions in the same database ( Such as Test2 Method )

/// <summary>
        /// B.  Multiple database technologies access transactions in the same database 
        /// </summary>
        public static void Test2()
        {
    
            var conStr = @"Server=localhost;Database=EFDB01;User ID=sa;Password=123456;";
            using (var connection = new SqlConnection(conStr))
            {
    
                connection.Open();
                using (var scope = new TransactionScope())
                {
    
                    try
                    {
    
                        //ADO.Net
                        var command = connection.CreateCommand();
                        command.CommandText = "DELETE FROM T_RoleInfor";
                        command.ExecuteNonQuery();

                        //EF
                        using (var context = new EFDB01Context1())
                        {
    
                            context.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                            context.SaveChanges();
                        }
                        // Comprehensive submission 
                        scope.Complete();

                        Console.WriteLine(" succeed ");

                    }
                    catch (Exception ex)
                    {
    
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }

C. Multiple different contexts of the same database are supported ( Such as Test3 Method )

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

Run code :

/// <summary>
        ///C.  Two different contexts of the same database are supported 
        /// </summary>
        public static void Test3()
        {
    
            using (var scope = new TransactionScope())
            {
    
                try
                {
    
                    using (var context = new EFDB01Context1())
                    {
    
                        context.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                        context.SaveChanges();
                    }
                    using (var context = new EFDB01Context2())
                    {
    
                        context.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                        context.SaveChanges();
                    }

                    // Comprehensive submission 
                    scope.Complete();

                    Console.WriteLine(" succeed ");
                }
                catch (Exception ex)
                {
    
                    Console.WriteLine(ex.Message);
                }
            }
        }

D. Different database contexts are not supported ,( Such as Test4 Method , Turn on msdtc Service steps : cmd command → net start msdtc , And then I found that there was a mistake :This platform does not support distributed transactions. Explain that at present Core Distributed transactions are not supported on the platform )

Context code :

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

Run code :

/// <summary>
        ///D.  Transactions between different databases 
        /// </summary>
        public static void Test4()
        {
    
            using (var scope = new TransactionScope())
            {
    
                try
                {
    
                    using (var context = new EFDB01Context1())
                    {
    
                        context.T_RoleInfor.Add(new T_RoleInfor() {
     roleName = " Administrators 1", addTime = DateTime.Now });
                        context.SaveChanges();
                    }
                    using (var context = new dbCore1Context())
                    {
    
                        context.UserInfors.Add(new UserInfors() {
     id = Guid.NewGuid().ToString("N"), userName = " Administrators 1", userSex = " male " });
                        context.SaveChanges();
                    }

                    // Comprehensive submission 
                    scope.Complete();

                    Console.WriteLine(" succeed ");
                }
                catch (Exception ex)
                {
    
                    Console.WriteLine(ex.Message);
                }
            }
        }

notes :EF Core Medium System.Transactions The implementation will not include support for distributed transactions , So it can't be used
TransactionScope or CommittableTransaction To coordinate transactions across multiple resource managers . Major distributed transactions need to rely on
Windows Systematic MSDTC service , but .NET Core To achieve cross platform , There is no unified standard for cross platform distributed transactions , The following version hopes to improve .

原网站

版权声明
本文为[I left my life to work]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/174/202206232109480783.html