交易範圍
TransactionScope 類別會提供一種簡易方式,讓您將某個程式碼區塊標示為參與交易。交易範圍可以自動選取和管理存在交易。當您使用 new 陳述式來具現化 TransactionScope 時,交易管理員就會決定要參與哪個交易。如果交易範圍 (亦即,在初始化 TransactionScope 物件與呼叫其 Dispose 方法之間) 中沒有發生例外狀況,此範圍所參與的交易就可以繼續。如果交易範圍中發生例外狀況,它所參與的交易將會回復。當應用程式完成它想要在交易中執行的所有工作時,就會呼叫一次 Complete 方法。這會通知交易管理員,表示認可該項交易是可接受的行為。如果沒有呼叫 Complete 方法,交易就會結束。如需有關交易範圍的詳細資訊,請參閱 MSDN 文件集。
.NET Framework 中的交易範圍實作
System.Transactions 命名空間屬於 Microsoft .NET Framework 2.0 版 3.0 和 3.5 版的一部分。它會提供與 ADO.NET 和 SQL Server Common Language Runtime (CLR) 整合完全整合的交易架構。System.Transactions.transactionscope 類別可藉由在分散式交易中隱含地編列連接,使程式碼區塊可進行交易。系統可能會在 TransactionScope 所標示的程式碼區塊結尾呼叫 Complete 方法。如果呼叫 Dispose 方法之前沒有呼叫 Complete 方法,交易就會中斷。如果擲回例外狀況,系統就會將交易視為即將中斷。
如需詳細資訊,請參閱 https://msdn2.microsoft.com/en-us/library/ms172070(VS.80).。
System.Transactions 的限制
.NET Compact Framework 2.0 不支援 System.Transactions 命名空間。因此,實作僅適用於 Windows 桌上型作業系統,而且將與 .NET Framework 2.0、.NET Framework 3.0 或 .NET Framework 3.5 相對應。
請注意,如果存在逾時,System.Transactions 基礎結構將會從個別的執行緒呼叫 Rollback。主要執行緒將不會得知在個別執行緒中發生之回復的相關資訊。長交易可能會看見不具決定性的行為和部分認可狀況。若要解決此問題,請在建立交易範圍物件時,增加此物件的時間範圍。
在交易範圍中,如果沒有其他交易管理員已經編列在交易範圍中,就只能編列一個 SqlCeConnection 物件。
如果某個連接是在交易範圍外部開啟的,而且它必須編列於現有的交易範圍中,您就可以使用 EnlistTransaction 方法來完成此作業。
TransactionScope 實作
SQL Server Compact 3.5 編列為 System.Transactions 基礎結構的資源。
根據預設,在某個交易範圍中的已編列連接上開啟多個命令時,這些命令會編列在目前交易內容中。此外,您也可以開啟沒有編列在交易範圍內的連接。這樣會建立未編列的命令。預設的交易類型可針對含有交易範圍的 SQL Server Compact 3.5 交易序列化。
由於 SQL Server Compact 3.5 不支援分散式交易,因此兩個以上的連接無法編列在相同的交易範圍內,也無法編列在共用相同存在交易範圍的巢狀交易範圍內。
目前不允許在交易範圍內部存在已編列連接的明確交易。
不支援隱含編列的連接。若要在交易範圍內編列,您可以進行下列動作:
在交易範圍內開啟連接。
或者,如果連接已經開啟,請針對連接物件呼叫 EnlistTransaction 方法。
SQL Server Compact 的限制
SQL Server Compact 3.5 與 SQL Server 之間有關交易範圍的差異如下:
SQL Server Compact 3.5 不支援分散式交易。因此,本機交易不會自動提升為完全可分散交易。
SQL Server Compact 3.5 支援平行交易,即使是 Multiple Active Result Sets (MARS) 也一樣。但是,SQL Server 不支援平行交易。
TransactionScope 範例 1
下列範例說明如何使用 TransactionScope 來編列並認可交易。
using (TransactionScope transScope = new TransactionScope())
{
using (SqlCeConnection connection1 = new
SqlCeConnection(connectString1))
{
/* Opening connection1 automatically enlists it in the
TransactionScope as a lightweight transaction. */
connection1.Open();
// Do work in the connection.
}
// The Complete method commits the transaction.
transScope.Complete();
}
TransactionScope 範例 2
下列範例說明如何使用 TransactionScope,在資料庫中建立兩份資料表。
static void Setup(String strDbPath)
{
/* Delete the database file if it already exists. We will create a new one. */
if (File.Exists(strDbPath))
{
File.Delete(strDbPath);
}
// Create a new database.
SqlCeEngine engine = new SqlCeEngine();
engine.LocalConnectionString = @"Data source = " + strDbPath;
engine.CreateDatabase();
engine.Dispose();
}
/* This function creates two tables in the specified database. Before creating the tables, it re-creates the database.
These tables are created in a TransactionScope, which means that either both of them will be created or not created at all. */
static void CreateTablesInTransaction(String strDbPath)
{
/* Create the connection string. In order to have the connection enlisted into the TransactionScope, the Enlist property in the connection string must be explicitly set to true. */
String strConn = @"Data source = " + strDbPath + ";Enlist=true";
SqlCeConnection conn = new SqlCeConnection(strConn);
try
{
Setup(strDbPath); // Create a new database for our tables.
using (TransactionScope scope = new TransactionScope())
{
/* To enlist a connection into a TransactinScope, specify 'Enlist=true' in its connection string and open the connection in the scope of that TransactionScope object. */
conn.Open();
// Create the tables.
SqlCeCommand command = conn.CreateCommand();
command.CommandText = @"create table t1(col1 int)";
command.ExecuteNonQuery();
command.CommandText = @"create table t2(col1 int)";
command.ExecuteNonQuery();
/* If this statement is executed and the TransactionScope has not timed out, t1 and t2 will be created in the specified database. */
scope.Complete();
}
}
catch (SqlCeException e)
{
Console.WriteLine(e.Message);
}
finally
{
if (conn.State != System.Data.ConnectionState.Closed)
{
conn.Close();
}
conn.Dispose();
}
}
TransactionScope 範例 3
下列範例說明如何使用 TransactionScope,將值插入兩份資料表中。
/* This function assumes that tables t1(col1 int) and t2(col1 int) are already created in the specified database. The condition for the following function is this:
If INSERTs into the first table succeed, then INSERT into the second table. However, if the INSERTs into the second table fail, roll back the inserts in the second table but do not roll back the inserts in the first table. Although this can also be done by way of regular transactions, this function demonstrates how to do it using TransactionScope objects. */
static void CreateTableAndInsertValues(String strDbPath)
{
/* Create the connection string. To have the connection enlisted into the TransactionScope, the Enlist property in the connection string must be explicitly set to true. */
String strConn = @"Data source = " + strDbPath + ";Enlist=true";
SqlCeConnection conn1 = new SqlCeConnection(strConn);
SqlCeConnection conn2 = new SqlCeConnection(strConn);
try
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
conn1.Open();
SqlCeCommand command1 = conn1.CreateCommand();
command1.CommandText = @"insert into t1(col1) values(1)";
command1.ExecuteNonQuery();
command1.CommandText = @"insert into t1(col1) values(2)";
command1.ExecuteNonQuery();
/* If this statement is executed and the TransactionScope has not timed out, two records will be inserted into table 1. */
scope.Complete();
try
{
using (TransactionScope scopeInner = new TransactionScope(TransactionScopeOption.RequiresNew))
{
conn2.Open();
SqlCeCommand command2 = conn2.CreateCommand();
command2.CommandText = @"insert into t2(col1) values(1)";
command2.ExecuteNonQuery();
command2.CommandText = @"insert into t2(col1) values(2)";
command2.ExecuteNonQuery();
/* If this statement is run and the TransactionScope has not timed out, two records will be inserted into table 2. */
scopeInner.Complete();
}
}
catch (SqlCeException e)
{
Console.WriteLine(@"Exception in Inner block: " + e.Message);
}
}
}
catch (SqlCeException e)
{
Console.WriteLine(@"Exception in Outer block: " + e.Message);
}
finally
{
// Close both the connections.
if (conn1.State != System.Data.ConnectionState.Closed)
{
conn1.Close();
}
if (conn2.State != System.Data.ConnectionState.Closed)
{
conn2.Close();
}
conn1.Dispose();
conn2.Dispose();
}
}