c#事务的使用、示例及注意事项 转
.NET Framework 开发员指南
事务是一组组合成逻辑工作单元的操作,虽然系统中可能会出错,但事务将控制和维护事务中每个操作的一致性和完整性。
例如,在将资金从一个帐户转移到另一个帐户的银行应用中,一个帐户将一定的金额贷记到一个数据库表中,同时另一个帐户将相同的金额借记到另一个数据库表中。由于计算机可能会因停电、网络中断等而出现故障,因此有可能更新了一个表中的行,但没有更新另一个表中的行。如果数据库支持事务,则可以将数据库操作组成一个事务,以防止因这些事件而使数据库出现不一致。如果事务中的某个点发生故障,则所有更新都可以回滚到事务开始之前的状态。如果没有发生故障,则通过以完成状态提交事务来完成更新。
在 ADO.NET 中,可以使用 Connection 和 Transaction 对象来控制事务。可以使用 Connection.BeginTransaction 启动本地事务。一旦开始一个事务,就可以使用 Command 对象的 Transaction 属性在该事务中登记命令。然后,可以根据事务组件的成功或失败情况,使用 Transaction 对象提交或回滚在数据源中所做的修改。
还可以使用 Connection.EnlistDistributedTransaction 在现有的分布式事务中登记。在现有的分布式事务中登记可以确保当提交或回滚整个分布式事务时,也提交或回滚对数据源所作的代码修改。
以下示例创建一个 OleDbConnection 和一个 OleDbTransaction。它还演示了如何使用 BeginTransaction、Commit 和 Rollback 方法。
{
OleDbConnection myConnection = new OleDbConnection(myConnString);
myConnection.Open();
OleDbCommand myCommand = myConnection.CreateCommand();
OleDbTransaction myTrans;
// Start a local transaction
myTrans = myConnection.BeginTransaction(IsolationLevel.ReadCommitted);
// Assign transaction object for a pending local transaction
myCommand.Connection = myConnection;
myCommand.Transaction = myTrans;
try
{
myCommand.CommandText = /"Insert into Region (RegionID, RegionDescription) VALUES (100, /'Description/')/";
myCommand.ExecuteNonQuery();
myCommand.CommandText = /"Insert into Region (RegionID, RegionDescription) VALUES (101, /'Description/')/";
myCommand.ExecuteNonQuery();
myTrans.Commit();
Console.WriteLine(/"Both records are written to database./");
}
catch(Exception e)
{
try
{
myTrans.Rollback();
}
catch (OleDbException ex)
{
if (myTrans.Connection != null)
{
Console.WriteLine(/"An exception of type /" + ex.GetType() +
/" was encountered while attempting to roll back the transaction./");
}
}
Console.WriteLine(/"An exception of type /" + e.GetType() +
/" was encountered while inserting the data./");
Console.WriteLine(/"Neither record was written to database./");
}
finally
{
myConnection.Close();
}
}
OleDbTransaction.Commit 方法
提交数据库事务。
public virtual void Commit();
OleDbTransaction.Rollback 方法
从挂起状态回滚事务。
public virtual void Rollback();
OleDbConnection.BeginTransaction 方法
开始数据库事务。
public OleDbTransaction BeginTransaction();
以当前的 IsolationLevel 值开始数据库事务。
public OleDbTransaction BeginTransaction(IsolationLevel);
IsolationLevel 枚举?
指定连接的事务锁定行为。 在执行事务时,.NET Framework 数据提供程序使用 IsolationLevel 值。在显式更改之前,IsolationLevel 保持有效,但是可以随时对它进行更改。新值在执行时使用,而不是在分析时使用。如果在事务期间更改,服务器的预期行为是,对其余所有语句应用新的锁定级别。
IsolationLevel成员 ReadCommitted
在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。
OleDbConnection.CreateCommand 方法
创建和返回一个与 OleDbConnection 相关联的 OleDbCommand 对象。
public OleDbCommand CreateCommand();
OleDbCommand.Connection 属性
获取或设置 OleDbCommand 的此实例使用的 OleDbConnection。
public OleDbConnection Connection {get; set;}
如何在.NET中实现事务(1)
如何在.NET中实现事务机制呢? 通常可以使用2种方式: 直接写入到sql 中;使用ADO.NET 实现。下面依次作一下介绍:
方法1:直接写入到sql 中
使用 BEGIN TRANS, COMMIT TRANS, ROLLBACK TRANS 实现:
例如
BEGIN TRANS
DECLARE @orderDetailsError int, @productError int
DELETE FROM /"Order Details/" WHERE ProductID=42
SELECT @orderDetailsError = @@ERROR
DELETE FROM Products WHERE ProductID=42
SELECT @productError = @@ERROR
IF @orderDetailsError = 0 AND @productError = 0
COMMIT TRANS
ELSE
ROLLBACK TRANS
这种方法比较简单,具体可以查阅相关sql server 帮助
方法2 :使用ADO.NET 实现,使用这种方式的优点是可以在中间层来管理事务,当然你也可以选择在数据层来实现。
SqlConnection 和OleDbConnection 对象有一个 BeginTransaction 方法,它可以返回 SqlTransaction 或者OleDbTransaction 对象。而且这个对象有 Commit 和 Rollback 方法来管理事务,具体例子如下:
Dim trans As SqlTransaction = cnNorthwind.BeginTransaction()
Dim cmDel As New SqlCommand()
cmDel.Connection = cnNorthwind
cmDel.Transaction = trans
Try
cmDel.CommandText = _
/"DELETE [Order Details] WHERE ProductID = 42/"
cmDel.ExecuteNonQuery()
cmDel.CommandText = /"DELETE Products WHERE ProductID = 42/"
cmDel.ExecuteNonQuery()
trans.Commit()
Catch Xcp As Exception
trans.Rollback()
Finally
cnNorthwind.Close()
End Try
Ok,通过上面的例子可以实现与方法1同样的效果。
并发问题:
如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题。并发问题包括: 丢失或覆盖更新,未确认的相关性(脏读),不一致的分析(非重复读),幻像读。但是如何来避免数据读取时脏读等问题出现呢?
二、事务实例
using(SqlTransaction trans = conn.BeginTransaction())
{
try
{
//循环进行信息的插入
for(int count = 0; count < applyInfo.Length; count ++)
{
//声明参数并赋值
SqlParameter[] parms =
{ Database.MakeInParam (/"@Stu_ID/",System.Data.SqlDbType.VarChar,11,applyInfocount].StuID),
Database.MakeInParam/"@Bank_Name/",System.Data.SqlDbType.VarChar,50,applyInfo[count].BankName), Database.MakeInParam/"@Apply_Loan_Money/",System.Data.SqlDbType.Money,8,applyInfo[count].ApplyLoanMoney), Database.MakeInParam(/"@Apply_Loan_Year/",System.Data.SqlDbType.VarChar,20,applyInfo[count].ApplyLoanYear), Database.MakeInParam/"@Apply_Year/",System.Data.SqlDbType.Char,6,applyInfo[count].ApplyYear), Database.MakeInParam(/"@Apply_Length/",System.Data.SqlDbType.Int,4,applyInfo[count].ApplyLength), Database.MakeInParam(/"@Apply_Pass/",System.Data.SqlDbType.Char,1,applyInfo[count].ApplyPass),
Database.MakeInParam(/"@Apply_Remark/",System.Data.SqlDbType.VarChar,100,applyInfo[count].ApplyRemark)
};
//执行新增操作
SqlHelper.ExecuteNonQuery(trans,CommandType.StoredProcedure, /"ApplyInfo_Create/", parms);
}
//未出现错误,则提交事务
trans.Commit();
return true;
}
catch(Exception ex)
{
//出错则回滚
trans.Rollback();
throw ex;
}
}
四、注意事项
事务的定义必须在连接打开后,提交必须在关闭以前
使用事务时必须即是把事务添加到sqlCommand中去。