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 Product;

update += "update Products set unitPrice=unitprice-18 where product; //因CHECK约束,将失败

update += "update Products set unitPrice=15 where product;

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 Product, cnNorthwind);

cmdProducts.Transaction = tranDebug;

cmdProducts.ExecuteNonQuery();

//下面行下断点,在MSMS查看数据记录是否可修改,并尝试修改:发现能修改数据

cmdProducts.CommandText = "Update products set UnitPrice=UnitPrice-18 where Product;

cmdProducts.ExecuteNonQuery();

//下面行下断点,因上面的Update导致数据被锁,无法在MSMS环境查看和修改数据

cmdProducts.CommandText = "select * from products where Product;

cmdProducts.ExecuteNonQuery();

//下面行下断点,发现仍无法读取数据,因为前面的Update已经锁定记录

throw new Exception("Rollback()");

tranDebug.Commit();

}

catch (SqlException ex)

{

tranDebug.Rollback();

Console.Write(ex.Message);

//throw;

}

finally

{

cnNorthwind.Dispose();

}

}

}