• TransactionScope事务简介


    原文:

    在.NET 1.0/1.1 版本我们使用SqlTransaction.处理事务
    string connString = ConfigurationManager.ConnectionStrings["db"].ConnectionString;
    using (var conn = new SqlConnection(connString))
    {
        conn.Open();
        using (IDbTransaction tran = conn.BeginTransaction())
        {
            try
            {
                // transactional code...
                using (SqlCommand cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "INSERT INTO Data(Code) VALUES('A-100');";
                    cmd.Transaction = tran as SqlTransaction;
                    cmd.ExecuteNonQuery();
                }
                tran.Commit();
            }
            catch(Exception ex)
            {
                tran.Rollback();
                throw;
            }
        }
    }
    到了.NET2.0微软提供了TransactionScope
    using (var scope = new TransactionScope())
    {   
     //transctional code…    
    scope.Complete();
    }
    TransactionScope使用起来简单,只需要把代码写在大括号里,最后加上scope.Complete();就可以了
     
    接下来主要介绍TransactionScope的简单使用
    TransactionScope有三个属性:IsolationLevel,Timeout,TransactionScopeOption

       Property
       
       
       Default Value
       
       
       Available Options
       
      
       IsolationLevel(隔离等级)
       
       
       Serializable
       
       
       Serializable, Read Committed, Read Un Committed, Repeatable Read
       
      
       Timeout(超时)
       
       
       1 Minute
       
       
       Maximum 10 Minutes
       
      
       TransactionScopeOption
       (选项)
       
       
       Required
       
       
       Required, Required New, Suppress
       
      用以下代码可以看到他们属性的值
    using (var scope = new System.Transactions.TransactionScope()){    System.Transactions.IsolationLevel isolationLevel = Transaction.Current.IsolationLevel;    TimeSpan defaultTimeout = TransactionManager.DefaultTimeout;    TimeSpan maximumTimeout = TransactionManager.MaximumTimeout;}
     
    由图可以看到,TransactionScope默认情况下的隔离等级为Serializable,超时时间为1分钟
     
    Dirty Read(脏读):脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
    Non Repeatable Read(不可重复读):是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)
    Phantom Read(幻读):是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象
    发生了幻觉一样。
     
    隔离等级的四种选择:
    Serializable(序列化):它在读/写操作时锁定数据。由于这个原因,很多时候它会创建一个死锁,因此你可能会得到一个超时异常。可以将此隔离级别用于高度安全的事务性应用程序(如金融应用程序)。缺点是性能低
    Repeatable Read(可重复读):同为Serializable,除了允许幻读。可以使用在金融中的应用或严重事务性应用,但需要知道幻读创造的场景是不存在的。
    Read Committed(读提交):大多数应用程序都可以使用它。SQLServer默认隔离级别是这个。不会有脏读
    Read Un-Committed(读不提交):这些应用程序不需要支持并发事务。
     
    TransactionScope使用:
    对CommonTest_ExamInfo_ExamStudents表中的一个学生进行修改操作,隔离等级为Read Committed
    var option = new TransactionOptions();                option.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;                option.Timeout = TimeSpan.FromMinutes(1);                using (var scope = new TransactionScope(TransactionScopeOption.Required, option))                {                    String sql = @"UPDATE Students SET sStuName = @sStuName WHERE sID = @sID AND sIdentityID = @sIdentityID ";                    SqlParameter[] args = new SqlParameter[3];                    args[0] = new SqlParameter("@sStuName", "张三");                    args[1] = new SqlParameter("@sTID", "2017120608575");                    args[2] = new SqlParameter("@sIdentityID", "36281763861231231");                    if (testDB.Database.ExecuteSqlCommand(sql, args) > 0)                    {                        scope.Complete();                        return true;                    }                    else                    {                        return false;                    }}
     
    当执行完代码红色那行后,可以看到,SQLserver启动了隔离等级为Read Committed的事务,然后执行修改操作。 此时,如果执行
    SELECT TOP 1000  * FROM [dbo].[Students]  SQL语句,如正在修改的数据也包括在内,则不能被查出来,修改操作也是如此。
     
    直到跳出using括号后,查询语句才能被真正执行。
    但是,此时数据库可以执行与事务修改操作无关的数据,如执行
    SELECT * FROM Students
    WHERE sTID = '2017120608575'  AND sIdentityID = '36281763861231231'    SQL语句,可以顺利查询到数据
     
    此外,还有一点需要说明,当连续执行两次相同的修改操作,即第二次其实是没有真正修改数据库数据的,此时可以对操作的数据行查询
     
    场次安排不使用事务和使用TransactionScope的Read Committed进行对比
     
    不使用事务如下:
     
     
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted })){                    testDB.Database.ExecuteSqlCommand("delete from dbo.ChildTestList where sTID=@sTid;delete from dbo.ExamStudents where sTID=@sTid;", new SqlParameter("@sTid", sTID));                    if (testDB.Database.Connection.State != ConnectionState.Open)                    {                        testDB.Database.Connection.Open(); //打开Connection连接                      }                   if(!DbHelper.BulkInsert<ExamStudents>((SqlConnection)testDB.Database.Connection, "ExamStudents", lstExamStudents))                    {                        if (testDB.Database.Connection.State != ConnectionState.Closed)                        {                            testDB.Database.Connection.Close(); //关闭Connection连接                          }                        return false;                    }                    if (!DbHelper.BulkInsert<ChildTestList>((SqlConnection)testDB.Database.Connection, "ChildTestList", lstCldTestTB))                    {                        if (testDB.Database.Connection.State != ConnectionState.Closed)                        {                            testDB.Database.Connection.Close(); //关闭Connection连接                          }                        return false;                    }                     if (testDB.Database.Connection.State != ConnectionState.Closed)                    {                        testDB.Database.Connection.Close(); //关闭Connection连接                      }                    var testInfo = testDB.TestList.Where(p => p.sTID == sTID).FirstOrDefault();                    if (nSaveType == 1)                    {                        testInfo.nTestState = 14;                    }                    else                    {                        testInfo.nTestState = 13;                    }                    testDB.SaveChanges();                    scope.Complete();                    return true;           }
    添加TransactionScope事务:
     
     
    以上两图显示他们性能上并无明显区别。
     
    总结:可以使用TransactionScope的Read Committed隔离等级进行一系列事务操作,性能没有太大影响。一般不使用TransactionScope默认的Serializable隔离等级,因为它需要开很多锁,性能会因此而下降。使用TransactionScope应注意尽量让using大括号里面的代码都短一些,这样它出错的几率更小,事务回滚的几率也会减少。所以出于数据的一致性考虑,一系列对数据库的操作可添加TransactionScope的Read Committed隔离等级的事务。
    ————————————————
    原文链接:https://blog.csdn.net/qq_35633131/article/details/82692854
  • 相关阅读:
    基于范围的for循环
    ML.NET技术研究系列-2聚类算法KMeans
    SQLServer常用运维SQL整理
    ML.NET技术研究系列-1入门篇
    Kafka基本知识整理
    .NetCore技术研究-EntityFramework Core 3.0 Preview
    容器技术研究-Kubernetes基本概念
    特来电混沌工程实践-混沌事件注入
    .Net Core技术研究-Span<T>和ValueTuple<T>
    Visual Studio Git本地Repos和GitHub远程Repos互操作
  • 原文地址:https://www.cnblogs.com/zhang1f/p/12902805.html
Copyright © 2020-2023  润新知