ADO.NET事务拾遗

[b]代码段1:[/b]
using (SqlConnection conn1 = new SqlConnection(connString))
{
if (conn1.State == ConnectionState.Closed)
conn1.Open();
SqlTransaction trans = conn1.BeginTransaction(IsolationLevel.ReadUncommitted);
try
{
SqlCommand cmd1 = new SqlCommand();
cmd1.Connection = conn1;
cmd1.CommandText = insertString1;//插入语句1
cmd1.Transaction = trans;
cmd1.ExecuteNonQuery();
cmd1.CommandText = insertString2;//插入语句2
cmd1.Transaction = trans;
cmd1.ExecuteNonQuery();

SqlCommand cmd2 = new SqlCommand(selectString, conn1);//查询语句
SqlDataAdapter sda = new SqlDataAdapter(cmd2);
DataSet ds =new DataSet();
sda.Fill(ds);

trans.Commit();
}
catch(SqlException ex){
trans.Rollback();
Console.WriteLine(ex.Message);
}
}

[b]说明:insertString1、insertString2和selectString操作的是同一张表[/b]
1.运行这段代码,到第21行(sda.Fill(ds))会出现异常:“如果分配给命令的连接位于本地挂起事务中,ExecuteReader 要求命令拥有事务。命令的 Transaction 属性尚未初始化。”
原因:SqlDataAdapter在填充数据时使用SqlDataReader读取数据,事务是基于数据库连接的,cmd1和cmd2使用的是同一连接,执行完cmd1后,事务处于挂起状态,还未提交。为了保持事务的完整性(其实这个地方是原子性的体现),要求新的cmd2必须也使用同一事务。
[b]代码段2:将代码段1稍作修改,使cmd2使用新的连接conn2[/b]
using (SqlConnection conn1 = new SqlConnection(connString))
{
if (conn1.State == ConnectionState.Closed)
conn1.Open();
SqlTransaction trans = conn1.BeginTransaction(IsolationLevel.ReadUncommitted);
try
{
SqlCommand cmd1 = new SqlCommand();
cmd1.Connection = conn1;
cmd1.CommandText = insertString1;//插入语句1
cmd1.Transaction = trans;
cmd1.ExecuteNonQuery();
cmd1.CommandText = insertString2;//插入语句2
cmd1.Transaction = trans;
cmd1.ExecuteNonQuery();
//下面使cmd2使用新的连接conn2
using (SqlConnection conn2 = new SqlConnection(connString))
{
SqlCommand cmd2 = new SqlCommand(selectString, conn2);
SqlDataAdapter sda = new SqlDataAdapter(cmd2);
DataSet ds = new DataSet();
sda.Fill(ds);
}

trans.Commit();
}
catch(SqlException ex){
trans.Rollback();
Console.WriteLine(ex.Message);
}
}

在 sda.Fill(ds)时会抛出异常:“ 超时时间已到。在操作完成之前超时时间已过或服务器未响应”
原因:
在SQL Server 2005中运行下面的脚本,可以获得锁的信息:
select request_session_id,
resource_type,
resource_database_id,
resource_description ,
resource_associated_entity_id,
request_mode,
request_status
from sys.dm_tran_locks

会发现有两个会话(SQl Server中的两个进程)发生了冲突,会话1请求了一个排它锁,而会话2一直在等待同一资源的共享锁,因此会话2被阻塞,形成了死锁的情况,过了程序设置的超时时间,SQLServer2005会自动终止操作,在C#中就会捕捉到超时的异常。(虽然会话1中事务的隔离级别已手动设置为未提交读ReadUncommitted,但是回话2中默认隔离级别为已提交读ReadCommited,所以会话2依然会等待会话释放排它锁)

总结:以上例子仅供说明之用,在使用事务时,要根据业务设计好数据库操作顺序,并且恰当的设置隔离级别,避免此种情况的出现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值