C#环境中的事务Transaction示例

摘要:C#环境中的事务Transaction示例

为图方便省事,你可以在一个Command对象的CommandText属性指定多条以;分割的语句。这种情况下若没有事务,所有的语句都会被执行,若其中有语句出错,就导致了数据的不一致性。

当然我们也可以写存储过程,在SQLServer的数据库系统内建存储过程的语句若没有事务,多条语句中的部分语句失效,一样导致数据的不一致性:你可以在存储过程内部Try/Catch/BeginTransaction等。

 Connection对象BeginTransaction启动事务,然后将事务赋值给Command对象的Transaction属性即挂接了事务。即使没有Commit 和Rollback,若执行中出现错误,事务一样自动回滚,或者成功提交。

BeginTransaction可以指定隔离级别。ReadXXX不会对数据库加锁,尽管在事务中,外部程序仍然可以读取数据;但若事务中有Update语句,将导致数据库锁,外部程序不能继续读取数据。

尽量考虑在存储过程中使用事务,避免使用ADO的事务,因为ADO的事务可能导致数据库长时间处于锁定状态;而数据库内的存储过程中的事务往往不会长时间挂起事务。

下面是简单的示例,供参考:

 


/// 
        /// ADO事物处理:单一Command对象执行多条语句:
        /// 包括导致异常的语句的后续语句,都将被执行,若没有Transaction,修改为15。
        /// 
        private static void TransByADO()
        {
            string connect = "Data Source=.\\sqlexpress; Initial Catalog=NorthWind ; Integrated Security = true;";
            //Old value: UnitPrice=18
            string update = "Update Northwind.dbo.Products set UnitPrice=10 where ProductID=1;";
            update += "update Products set unitPrice=unitprice-18 where productID=1;"; //因CHECK约束,将失败
            update += "update Products set unitPrice=15 where productID=1";

            SqlTransaction tranProducts = null;

            using (SqlConnection cnNorthwind = new SqlConnection(connect))
            {
                try
                {

                    cnNorthwind.Open();
                    tranProducts = cnNorthwind.BeginTransaction();//--1

                    SqlCommand cmdUpdate = new SqlCommand(update, cnNorthwind);
                    cmdUpdate.Transaction = tranProducts;//-2
                    cmdUpdate.ExecuteNonQuery();
                    //如果没有Commit()和Rollback(),事物仍然生效,结果为原始值18
                    tranProducts.Commit(); //--3
                }
                catch (SqlException ex)
                {
                    tranProducts.Rollback();//--4
                    //throw ex;
                    Console.WriteLine(ex);
                }
                finally
                {
                    cnNorthwind.Close();
                }
            }

        }

/// 
        /// 存储过程中若有部分语句执行失败,在没有Transaction的情况下将导致数据不一致。
        //create procedure TransTest
        //as
        //begin
        //    update Products set unitPrice=10 where productID=1
        //    update Products set unitPrice=unitprice-18 where productID=1 
        //    update Products set unitPrice=15 where productID=1
        //end
        //go
        /// 
        private static void TransByStoreProcedure()
        {
            string connect = "Data Source=.\\sqlexpress; Initial Catalog=NorthWind ; Integrated Security = true;";
            using (SqlConnection cnNorthwind = new SqlConnection(connect))
            {


                SqlTransaction tranProducts = null;

                try
                {
                    cnNorthwind.Open();
                    tranProducts = cnNorthwind.BeginTransaction();//-----1

                    SqlCommand cmdProduct = new SqlCommand("TransTest", cnNorthwind);
                    cmdProduct.CommandType = CommandType.StoredProcedure;
                    cmdProduct.Transaction = tranProducts;    //----2
                    cmdProduct.ExecuteNonQuery();
                    //如果没有Commit()和Rollback(),事物仍然生效,结果为原始值18
                    tranProducts.Commit();  //----3
                }
                catch (SqlException ex)
                {
                    tranProducts.Rollback();  //----4
                    Console.WriteLine(ex.ToString());
                    //throw ex ;
                }
                finally
                {
                    cnNorthwind.Dispose();
                }
            }
        }

/// 
        /// BeginTransaction(IsolationLevel)定义事务的隔离级别
        /// ReadCommitted:默认项,读取时加共享锁,避免脏读,数据在事务完成前可修改,可被外部读取
        /// ReadUncommitted:可脏读,不发布共享锁,也无独占锁
        /// 
        private static void TransDebug()
        {
            string connect = "Data Source=.\\sqlexpress; Initial Catalog=NorthWind ; Integrated Security = true;";
            using (SqlConnection cnNorthwind = new SqlConnection(connect))
            {
                SqlCommand cmdProducts = null;
                SqlTransaction tranDebug = null;
                try
                {
                    cnNorthwind.Open();
                    tranDebug = cnNorthwind.BeginTransaction(IsolationLevel.ReadUncommitted);//可读未提交数据
                    cmdProducts = new SqlCommand("select * from products where ProductID=1", cnNorthwind);
                    cmdProducts.Transaction = tranDebug;
                    cmdProducts.ExecuteNonQuery();

                    //下面行下断点,在MSMS查看数据记录是否可修改,并尝试修改:发现能修改数据
                    cmdProducts.CommandText = "Update products set UnitPrice=UnitPrice-18 where ProductID=1";
                    cmdProducts.ExecuteNonQuery();
                    //下面行下断点,因上面的Update导致数据被锁,无法在MSMS环境查看和修改数据
                    cmdProducts.CommandText = "select * from products where ProductID=1";
                    cmdProducts.ExecuteNonQuery();
                    //下面行下断点,发现仍无法读取数据,因为前面的Update已经锁定记录
                    throw new Exception("Rollback()");
                    tranDebug.Commit();
                }
                catch (SqlException ex)
                {
                    tranDebug.Rollback();
                    Console.Write(ex.Message);
                    //throw;
                }
                finally
                {
                    cnNorthwind.Dispose();
                }
            }
        }