许明会的计算机技术主页

Language:C,C++,.NET Framework(C#)
Thinking:Design Pattern,Algorithm,WPF,Windows Internals
Database:SQLServer,Oracle,MySQL,PostSQL
IT:MCITP,Exchange,Lync,Virtualization,CCNP

导航

ADO.NET在C#环境中的事务Transaction示例

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

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

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

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

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

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

        /// <summary>
        
/// ADO事物处理:单一Command对象执行多条语句:
        
/// 包括导致异常的语句的后续语句,都将被执行,若没有Transaction,修改为15。
        
/// </summary>
        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();
                }
            }

        }


        /// <summary>
        
/// 存储过程中若有部分语句执行失败,在没有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
        /// </summary>
        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();
                }
            }
        }

 

        /// <summary>
        
/// BeginTransaction(IsolationLevel)定义事务的隔离级别
        
/// ReadCommitted:默认项,读取时加共享锁,避免脏读,数据在事务完成前可修改,可被外部读取
        
/// ReadUncommitted:可脏读,不发布共享锁,也无独占锁
        
/// </summary>
        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();
                }
            }
        }

 

posted on 2011-04-06 22:07  许明会  阅读(2514)  评论(0编辑  收藏  举报