1. 程式人生 > >C#中事務的處理方法

C#中事務的處理方法

什麼是資料庫事務  資料庫事務是指作為單個邏輯工作單元執行的一系列操作。設想網上購物的一次交易,其付款過程至少包括以下幾步資料庫操作:· 更新客戶所購商品的庫存資訊· 儲存客戶付款資訊--可能包括與銀行系統的互動· 生成訂單並且儲存到資料庫中· 更新使用者相關資訊,例如購物數量等等正常的情況下,這些操作將順利進行,最終交易成功,與交易相關的所有資料庫資訊也成功地更新。但是,如果在這一系列過程中任何一個環節出了差錯,例如在更新商品庫存資訊時發生異常、該顧客銀行帳戶存款不足等,都將導致交易失敗。一旦交易失敗,資料庫中所有資訊都必須保持交易前的狀態不變,比如最後一步更新使用者資訊時失敗而導致交易失敗,那麼必須保證這筆失敗的交易不影響資料庫的狀態

--庫存資訊沒有被更新、使用者也沒有付款,訂單也沒有生成。否則,資料庫的資訊將會一片混亂而不可預測。資料庫事務正是用來保證這種情況下交易的平穩性和可預測性的技術。  資料庫事務的ACID屬性事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向資料的資源。通過將一組相關操作組合為一個要麼全部成功要麼全部失敗的單元,可以簡化錯誤恢復並使應用程式更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和永續性)屬性:· 原子性事務必須是原子工作單元;對於其資料修改,要麼全都執行,要麼全都不執行。通常,與某個事務關聯的操作具有共同的目標,並且是相互依賴的。如果系統只執行這些操作的一個子集,則可能會破壞事務的總體目標。原子性消除了系統處理操作子集的可能性。
· 一致性事務在完成時,必須使所有的資料都保持一致狀態。在相關資料庫中,所有規則都必須應用於事務的修改,以保持所有資料的完整性。事務結束時,所有的內部資料結構(如 B 樹索引或雙向連結串列)都必須是正確的。某些維護一致性的責任由應用程式開發人員承擔,他們必須確保應用程式已強制所有已知的完整性約束。例如,當開發用於轉帳的應用程式時,應避免在轉帳過程中任意移動小數點。· 隔離性由併發事務所作的修改必須與任何其它併發事務所作的修改隔離。事務檢視資料時資料所處的狀態,要麼是另一併發事務修改它之前的狀態,要麼是另一事務修改它之後的狀態,事務不會檢視中間狀態的資料。這稱為可序列性,因為它能夠重新裝載起始資料,並且重播一系列事務,以使資料結束時的狀態與原始事務執行的狀態相同。當事務可序列化時將獲得最高的隔離級別。在此級別上,從一組可並行執行的事務獲得的結果與通過連續執行每個事務所獲得的結果相同。由於高度隔離會限制可並行執行的事務數,所以一些應用程式降低隔離級別以換取更大的吞吐量。
· 永續性 事務完成之後,它對於系統的影響是永久性的。該修改即使出現致命的系統故障也將一直保持。DBMS的責任和我們的任務企業級的資料庫管理系統(DBMS)都有責任提供一種保證事務的物理完整性的機制。就常用的SQL Server2000系統而言,它具備鎖定裝置隔離事務、記錄裝置保證事務永續性等機制。因此,我們不必關心資料庫事務的物理完整性,而應該關注在什麼情況下使用資料庫事務、事務對效能的影響,如何使用事務等等。本文將涉及到在.net框架下使用C#語言操縱資料庫事務的各個方面。

體驗SQL語言的事務機制作為大型的企業級資料庫,SQL Server2000對事務提供了很好的支援。我們可以使用SQL語句來定義、提交以及回滾一個事務。如下所示的SQL程式碼定義了一個事務,並且命名為"MyTransaction"(限於篇幅,本文並不討論如何編寫SQL語言程式,請讀者自行參考相關書籍):

DECLARE @TranName VARCHAR(20)

SELECT @TranName = 'MyTransaction'
BEGIN TRANSACTION @TranNameGOUSE pubs
GO

UPDATE roysched
SET royalty = royalty * 1.10
WHERE title_id LIKE 'Pc%'
GO

COMMIT TRANSACTION MyTransaction
GO

這裡用到了SQL Server2000自帶的示例資料庫pubs,提交事務後,將為所有暢銷計算機書籍支付的版稅增加 10%開啟SQL Server2000的查詢分析器,選擇pubs資料庫,然後執行這段程式,結果顯而易見。可是如何在C#程式中執行呢?我們記得在普通的SQL查詢中,一般需要把查詢語句賦值給SalCommand.CommandText屬性,這裡也就像普通的SQL查詢語句一樣,將這些語句賦給SqlCommand.CommandText屬性即可。要注意的一點是,其中的"GO"語句標誌著SQL批處理的結束,編寫SQL指令碼是需要的,但是在這裡是不必要的。我們可以編寫如下的程式來驗證這個想法:

//TranSql.csusing System;
using System.Data;
using System.Data.SqlClient;
namespace Aspcn
{
public class DbTranSql
{
file://將事務放到SQL Server中執行public void DoTran()
{
file://建立連線並開啟SqlConnection myConn=GetConn();myConn.Open();
SqlCommand myComm=new SqlCommand();
try
{
myComm.Connection=myConn;
myComm.CommandText="DECLARE @TranName VARCHAR(20) ";
myComm.CommandText+="SELECT @TranName = 'MyTransaction' ";
myComm.CommandText+="BEGIN TRANSACTION @TranName ";
myComm.CommandText+="USE pubs ";
myComm.CommandText+="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%' ";
myComm.CommandText+="COMMIT TRANSACTION MyTransaction ";
myComm.ExecuteNonQuery();
}
catch(Exception err)
{
throw new ApplicationException("事務操作出錯,系統資訊:"+err.Message);
}
finally
{
myConn.Close();
}
}
file://獲取資料連線private SqlConnection GetConn()
{
string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
SqlConnection myConn=new SqlConnection(strSql);
return myConn;
}
}

public class Test
{
public static void Main()
{
DbTranSql tranTest=new DbTranSql();
tranTest.DoTran();
Console.WriteLine("事務處理已經成功完成。");
Console.ReadLine();
}
}
}

  注意到其中的SqlCommand物件myComm,它的CommandText屬性僅僅是前面SQL程式碼字串連線起來即可,當然,其中的"GO"語句已經全部去掉了。這個語句就像普通的查詢一樣,程式將SQL文字事實上提交給DBMS去處理了,然後接收返回的結果(如果有結果返回的話)。很自然,我們最後看到了輸出"事務處理已經成功完成",再用企業管理器檢視pubs資料庫的roysched表,所有title_id欄位以"PC"開頭的書籍的royalty欄位的值都增加了0.1倍。這裡,我們並沒有使用ADO.net的事務處理機制,而是簡單地將執行事務的SQL語句當作普通的查詢來執行,因此,事實上該事務完全沒有用到.net的相關特性。瞭解.net中的事務機制如你所知,在.net框架中主要有兩個名稱空間(namespace)用於應用程式同資料庫系統的互動:System.Data.SqlClientSystem.Data.OleDb。前者專門用於連線Microsoft公司自己的SQL Server資料庫,而後者可以適應多種不同的資料庫。這兩個名稱空間中都包含有專門用於管理資料庫事務的類,分別是System.Data.SqlClient.SqlTranscation類和System.Data.OleDb.OleDbTranscation類。就像它們的名字一樣,這兩個類大部分功能是一樣的,二者之間的主要差別在於它們的連線機制,前者提供一組直接呼叫 SQL Server 的物件,而後者使用本機 OLE DB 啟用資料訪問。事實上,ADO.net 事務完全在資料庫的內部處理,且不受 Microsoft 分散式事務處理協調器 (DTC) 或任何其他事務性機制的支援。本文將主要介紹System.Data.SqlClient.SqlTranscation類,下面的段落中,除了特別註明,都將使用System.Data.SqlClient.SqlTranscation類。

事務的開啟和提交現在我們對事務的概念和原理都瞭然於心了,並且作為已經有一些基礎的C#開發者,我們已經熟知編寫資料庫互動程式的一些要點,即使用SqlConnection類的物件的Open()方法建立與資料庫伺服器的連線,然後將該連線賦給SqlCommand物件的Connection屬性,將欲執行的SQL語句賦給它的CommandText屬性,於是就可以通過SqlCommand物件進行資料庫操作了。對於我們將要編寫的事務處理程式,當然還需要定義一個SqlTransaction型別的物件。並且看到SqlCommand物件的Transcation屬性,我們很容易想到新建的SqlTransaction物件應該與它關聯起來。基於以上認識,下面我們就開始動手寫我們的第一個事務處理程式。我們可以很熟練地寫出下面這一段程式:

//DoTran.csusing System;
using System.Data;
using System.Data.SqlClient;
namespace Aspcn
{
public class DbTran
{
file://執行事務處理public void DoTran()
{
file://建立連線並開啟SqlConnection myConn=GetConn();
myConn.Open();
SqlCommand myComm=new SqlCommand();
SqlTransaction myTran=new SqlTransaction();
try
{
myComm.Connection=myConn;
myComm.Transaction=myTran;
file://定位到pubs資料庫 myComm.CommandText="USE pubs";
myComm.ExecuteNonQuery();

file://更新資料file://將所有的計算機類圖書myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";
myComm.ExecuteNonQuery();//提交事務myTran.Commit();
}
catch(Exception err)
{
throw new ApplicationException("事務操作出錯,系統資訊:"+err.Message);
}
finally
{
myConn.Close();
}
}
file://獲取資料連線private SqlConnection GetConn()
{
string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
SqlConnection myConn=new SqlConnection(strSql);
return myConn;
}
}

public class Test{public static void Main()
{
DbTran tranTest=new DbTran();
tranTest.DoTran();
Console.WriteLine("事務處理已經成功完成。");
Console.ReadLine();
}
}
}

顯然,這個程式非常簡單,我們非常自信地編譯它,但是,出乎意料的結果使我們的成就感頓時煙消雲散:error CS1501: 過載"SqlTransaction"方法未獲取"0"引數是什麼原因呢?注意到我們初始化的程式碼:

SqlTransaction myTran=new SqlTransaction();

顯然,問題出在這裡,事實上,SqlTransaction類並沒有公共的建構函式,我們不能這樣新建一個SqlTrancaction型別的變數。在事務處理之前確實需要有一個SqlTransaction型別的變數,將該變數關聯到SqlCommand類的Transcation屬性也是必要的,但是初始化方法卻比較特別一點。在初始化SqlTransaction類時,你需要使用SqlConnection類的BeginTranscation()方法:

SqlTransaction myTran; myTran=myConn.BeginTransaction();

該方法返回一個SqlTransaction型別的變數。在呼叫BeginTransaction()方法以後,所有基於該資料連線物件的SQL語句執行動作都將被認為是事務MyTran的一部分。同時,你也可以在該方法的引數中指定事務隔離級別和事務名稱,如:

SqlTransaction myTran;
myTran=myConn.BeginTransaction(IsolationLevel.ReadCommitted,"SampleTransaction");

關於隔離級別的概念我們將在隨後的內容中探討,在這裡我們只需牢記一個事務是如何被啟動,並且關聯到特定的資料鏈接的。先不要急著去搞懂我們的事務都幹了些什麼,看到這一行:

myTran.Commit();

是的,這就是事務的提交方式。該語句執行後,事務的所有資料庫操作將生效,並且為資料庫事務的永續性機制所保持--即使系統在這以後發生致命錯誤,該事務對資料庫的影響也不會消失。對上面的程式做了修改之後我們可以得到如下程式碼(為了節約篇幅,重複之處已省略,請參照前文):

//DoTran.cs……

file://
執行事務處理
public void DoTran()
{
file://建立連線並開啟SqlConnection myConn=GetConn();
myConn.Open();
SqlCommand myComm=new SqlCommand();

file://SqlTransaction myTran=new SqlTransaction();
file://注意,SqlTransaction類無公開的建構函式SqlTransaction myTran;

file://建立一個事務myTran=myConn.BeginTransaction();
try
{
file://從此開始,基於該連線的資料操作都被認為是事務的一部分file://下面繫結連線和事務物件myComm.Connection=myConn;
myComm.Transaction=myTran; file://定位到pubs資料庫myComm.CommandText="USE pubs";
myComm.ExecuteNonQuery();//更新資料file://將所有的計算機類圖書myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";
myComm.ExecuteNonQuery();
file://提交事務myTran.Commit();
}
catch(Exception err)
{
throw new ApplicationException("事務操作出錯,系統資訊:"+err.Message);
}
finally
{
myConn.Close();
}
}
……

到此為止,我們僅僅掌握瞭如何開始和提交事務。下一步我們必須考慮的是在事務中可以幹什麼和不可以幹什麼。

另一個走向極端的錯誤


滿懷信心的新手們可能為自己所掌握的部分知識陶醉不已,剛接觸資料庫庫事務處理的準開發者們也一樣,躊躇滿志地準備將事務機制應用到他的資料處理程式的每一個模組每一條語句中去。的確,事務機制看起來是如此的誘人——簡潔、美妙而又實用,我當然想用它來避免一切可能出現的錯誤——我甚至想用事務把我的資料操作從頭到尾包裹起來。


看著吧,下面我要從建立一個數據庫開始:

using System;
using System.Data;
using System.Data.SqlClient;

namespace Aspcn
{
public class DbTran
{
file://執行事務處理public void DoTran()
{
file://建立連線並開啟SqlConnection myConn=GetConn();
myConn.Open();

SqlCommand myComm=new SqlCommand();
SqlTransaction myTran;

myTran=myConn.BeginTransaction();

file://下面繫結連線和事務物件myComm.Connection=myConn;
myComm.Transaction=myTran;

file://試圖建立資料庫TestDB
myComm.CommandText="CREATE database TestDB";
myComm.ExecuteNonQuery();

file://提交事務myTran.Commit();
}

file://獲取資料連線private SqlConnection GetConn()
{
string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
SqlConnection myConn=new SqlConnection(strSql);
return myConn;
}
}

public class Test
{
public static void Main()
{
DbTran tranTest=new DbTran();
tranTest.DoTran();
Console.WriteLine("事務處理已經成功完成。");
Console.ReadLine();
}
}
}

//---------------

  未處理的異常: System.Data.SqlClient.SqlException: 在多語句事務內不允許使用 CREATE DATABASE 語句。

at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at Aspcn.DbTran.DoTran()
at Aspcn.Test.Main()

注意,如下的SQL語句不允許出現在事務中:

ALTER DATABASE

修改資料庫

BACKUP LOG

備份日誌

CREATE DATABASE

建立資料庫

DISK INIT

建立資料庫或事務日誌裝置

DROP DATABASE

刪除資料庫

DUMP TRANSACTION

轉儲事務日誌

LOAD DATABASE

裝載資料庫備份複本

LOAD TRANSACTION

裝載事務日誌備份複本

RECONFIGURE

更新使用 sp_configure 系統儲存過程更改的配置選項的當前配置(sp_configure 結果集中的 config_value 列)值。

RESTORE DATABASE

還原使用BACKUP命令所作的資料庫備份

RESTORE LOG

還原使用BACKUP命令所作的日誌備份

UPDATE STATISTICS

在指定的表或索引檢視中,對一個或多個統計組(集合)有關鍵值分發的資訊進行更新

除了這些語句以外,你可以在你的資料庫事務中使用任何合法的SQL語句。

事務回滾事務的四個特性之一是原子性,其含義是指對於特定操作序列組成的事務,要麼全部完成,要麼就一件也不做。如果在事務處理的過程中,發生未知的不可預料的錯誤,如何保證事務的原子性呢?當事務中止時,必須執行回滾操作,以便消除已經執行的操作對資料庫的影響。一般的情況下,在異常處理中使用回滾動作是比較好的想法。前面,我們已經得到了一個更新資料庫的程式,並且驗證了它的正確性,稍微修改一下,可以得到:

//RollBack.cs
using System;
using System.Data;
using System.Data.SqlClient;

namespace Aspcn
{
public class DbTran
{
file://執行事務處理public void DoTran()
{
file://建立連線並開啟SqlConnection myConn=GetConn();
myConn.Open();

SqlCommand myComm=new SqlCommand();
SqlTransaction myTran;

file://建立一個事務myTran=myConn.BeginTransaction();
file://從此開始,基於該連線的資料操作都被認為是事務的一部分file://下面繫結連線和事務物件myComm.Connection=myConn;
myComm.Transaction=myTran;

try
{
file://定位到pubs資料庫myComm.CommandText="USE pubs";
myComm.ExecuteNonQuery();
myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";
myComm.ExecuteNonQuery();

file://下面使用建立資料庫的語句製造一個錯誤myComm.CommandText="Create database testdb";
myComm.ExecuteNonQuery();

myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.20 WHERE title_id LIKE 'Ps%'";
myComm.ExecuteNonQuery();

相關推薦

C#事務處理方法

什麼是資料庫事務  資料庫事務是指作為單個邏輯工作單元執行的一系列操作。設想網上購物的一次交易,其付款過程至少包括以下幾步資料庫操作:· 更新客戶所購商品的庫存資訊· 儲存客戶付款資訊--可能包括與銀行系統的互動· 生成訂單並且儲存到資料庫中· 更新使用者相關資訊,例如購物數量等等正常的情況下,這些

TransactionScope事務處理方法介紹及.NET Core的注意事項

作者:依樂祝 原文連結:https://www.cnblogs.com/yilezhu/p/10170712.html 今天在寫CzarCms的UnitOfWork的使用使用到了這個TransactionScope事務,因此對它進行了相關資料的查閱並記錄如下,希望對大夥在.NET Core中使

中文在C/C++處理和漢字亂碼問題(wchar_t)

生成 setlocale 錯誤 並且 超過 函數 開發 長度 tracking 中文字在C/C++中的處理 現在編程的語言和編程環境隨著中國的發展開始對中文有進一步的支持。可是對中文的支持整體來說是有缺陷的,並且有與編譯環境的不同導

c#關於事務處理超時

blog c# 技術分享 detail 調試 關於 相關 tails 技術 今天早上一來 打開項目 進行功能調試 發現出現這個錯誤 看了一下提示 說是事務處理超時了 百度了一下相關的知識 http://blog.csdn.net/ahywg/article

C#的匿名方法實例解析

鏈接 而在 說明 程序設計 renren thum ont 好處 pre 本文較為詳細的講述了C#中的匿名方法,並附上實例加以說明。現將其分享給大家供大家參考之用。具體分析如下: 首先,C#中的匿名方法是在C#2.0引入的,它終結了C#2.0之前版本聲明委托的唯一方法是使

[翻譯]C#異步方法的性能特點

yield 類型 result begin 因此 保存 很大的 alloc involved 翻譯自一篇博文,原文:The performance characteristics of async methods in C# 異步系列 剖析C#中的異步方法 擴展C#中的異

Objective-C,類方法的getter和setter可以用點運算符嗎?

clang col 靜態變量 變量 uri family lan getc sta Objective-C中,對象實例property的getter和setter可以使用點運算符來操作,那麽類方法的getter和setter可以使用點運算嗎? 答案是肯定的。 看如下代碼

DbHelperSQL 增加事務處理方法(2種)

result nal clas nsa string null per lec login 方法一: 1 public static bool ExecuteSqlByTrans(List<SqlAndPrams> list) 2 { 3

46.一個例子瞭解C++異常處理

異常處理的棧展開、解構函式和建構函式的異常,異常處理的層次 catch的搜尋匹配 。。。 class wrong :public exception { public: wrong(const int& i = 0,const string& str = ""):err

C#的虛方法、抽象方法、抽象類以及接口

狀態 設置 基本 簡單的 語言 pre 語法 區別 自動生成 眾所周知,C#作為一門OOP(面向對象程序設計)語言,在許多地方都有與C++相似的地方,然而也有很多不同的地方。 說到面向對象,腦袋裏第一反應當然就是面向對象的三大原則(java中是四大原則): 封裝、繼承、多態

C#的虛方法、抽象方法、抽象類、介面的聯絡與區別

虛方法的關鍵字是virtual抽象方法的關鍵字是abstract重寫都是override 虛方法也可以new虛方法和抽象方法的區別:虛方法:可以在抽象類和非抽象類中定義,可以寫在父類中,在子類中可以被重寫,在定義虛方法時必須實現虛方法 (在定義虛方法時需要寫實現方法的程式碼或者至少

C#開啟事務處理

在C#中開啟事務的步驟 01.呼叫SqlConnection物件的BeginTransaction()方法,建立一個SqlTransaction物件,標誌事務開始。 02.將建立的SqlTransaction物件分配給要執行的SqlCommand的Transaction屬性。 03.呼叫

解決C#呼叫WCF方法報錯:遠端伺服器返回錯誤 (404) 未找到

IIS配置問題,解決方法: 1. 首先新增MIME型別 副檔名“.svc”,MIME型別 “application/octet-stream”  2、處理程式對映--新增託管處理程式 請求路徑 “.svc” 型別 “System.ServiceModel.Activation.HttpH

C# String.Join()方法

  今天在工作中看到了組裡一個大佬寫的程式碼,感觸頗多,同樣實現一個需求,我寫迴圈費了老大勁,程式碼又臭又長,大佬的程式碼簡潔明瞭,三行搞定。。。不得不說,今天賺大了   簡單總結一下今天賺到的知識   string裡邊的Join()方法    &nb

請問C#通過什麼方法可以獲取到訊息佇列總數

// 指數 -- 當對陣列按各位進行排序時,exp=1;按十位進行排序時,exp=10;... int exp; // 陣列a中的最大值 int max = getMax(arr); // 從個位開始,對陣列a按"指數"進行排序 for (exp = 1;

C#四捨五入的方法

在日常計算中,經常會對小數點後的數字進行四捨五入的操作。那麼在C#中,如何實現四捨五入呢? 1、使用 Round() 方法輸出 double Value = 1880.875; double d = Math.Round(Value, 2); //輸出:1880.88

VS2013 C# 的除錯方法

一、除錯方法     1、在程式的必要位置按F9(或用滑鼠點選行首出現斷電標誌處可設定斷點或取消斷點)。     2、按F5執行程式,程式執行到斷點會暫停下來等待使用者選擇下一步操作。     3、這時按F11為單步執行:遇到呼叫其他程式塊時會進入該模組的內部一步一

C# mysql 事務處理

try { using (MySqlConnection conn = new MySqlConnection("Database=Database;Data Source=127.0.0.1;User Id=root;Password=1234

C#的靜態方法和靜態變數的一些總結

方法: static 修飾符的方法為靜態方法,反之則是非靜態方法 靜態成員屬於類所有,非靜態成員屬於類的例項所有,無論類建立了多少例項,類的靜態成員在記憶體中只佔同一塊區域。(所有該類的例項都共享這個類的靜態成員) C#靜態方法屬於類所有,類例項化前即可使用,靜態方法只能訪

C++異常處理

異常處理        try-throw-catch是丟擲和捕捉異常的基本機制,throw語句丟擲異常(一個值),catch塊捕捉異常。丟擲異常後,try塊終止,轉而執行catch塊的程式碼。Catch塊結束後,會據需執行catch塊之後的程式碼(前提是catch塊沒有終