介紹
.NET Framework 開發員指南
事務是一組組合成邏輯工作單元的操作,雖然系統中可能會出錯,但事務將控制和維護事務中每個操作的一致性和完整性。
例如,在將資金從一個帳戶轉移到另一個帳戶的銀行套用中,一個帳戶將一定的金額貸記到一個資料庫表中,同時另一個帳戶將相同的金額借記到另一個資料庫表中。由於計算機可能會因停電、網路中斷等而出現故障,因此有可能更新了一個表中的行,但沒有更新另一個表中的行。如果資料庫支持事務,則可以將資料庫操作組成一個事務,以防止因這些事件而使資料庫出現不一致。如果事務中的某個點發生故障,則所有更新都可以回滾到事務開始之前的狀態。如果沒有發生故障,則通過以完成狀態提交事務來完成更新。
在 .net 中,可以使用 Connection 和 Transaction 對象來控制事務。可以使用 Connection.BeginTransaction 啟動本地事務。一旦開始一個事務,就可以使用 Command 對象的 Transaction 屬性在該事務中登記命令。然後,可以根據事務組件的成功或失敗情況,使用 Transaction 對象提交或回滾在數據源中所做的修改。
還可以使用 Connection.EnlistDistributedTransaction 在現有的分散式事務中登記。在現有的分散式事務中登記可以確保當提交或回滾整個分散式事務時,也提交或回滾對數據源所作的代碼修改。
以下示例創建一個 OleDbConnection 和一個 OleDbTransaction。它還演示了如何使用 BeginTransaction、Commit 和 Rollback 方法。
public void RunOleDbTransaction(string myConnString)
{
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 中;使用.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 :使用.net實現,使用這種方式的優點是可以在中間層來管理事務,當然你也可以選擇在數據層來實現。
SqlConnection 和OleDbConnection 對象有一個 BeginTransaction 方法,它可以返回 SqlTransaction 或者OleDbTransaction 對象。而且這個對象有 Commit 和 Rollback 方法來管理事務,具體例子如下:
cnNorthwind.Open()
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中去。