[英]TransactionScope across multiple threads using Task.WhenAll
我正在嘗試使用 Task.WhenAll 對數據庫進行多個並行更新。 代碼流程是這樣的。
在主要方法中,我創建了一個事務 scope 並創建了主要事務的克隆並傳遞給子事務。 主事務被阻塞,直到 child 完成
using (var scope = DalcHelper.GetTransactionScope())
{
DependentTransaction transaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
var task1= Dalc.UpdateDetails1(transaction );
DependentTransaction transaction1 = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
var task2 = Dalc.UpdateDetails2(transaction1);
await Task.WhenAll(task1, task2 ).ConfigureAwait(false);
scope.Complete();
}
DalcMethod 是這樣的。 這里從外部事務創建的克隆作為參數。 依賴事務完成通知主事務依賴完成
try
{
using (SqlCommand databaseCommand = DalcHelper.GetCommand(SPName))
using (var scope = new TransactionScope(dependentCloneTransaction, TransactionScopeAsyncFlowOption.Enabled))
{
-- Update database
scope.Complete();
}
}
finally
{
//Call complete on the dependent transaction
dependentCloneTransaction.Complete();
}
Dalc 方法是返回 Task 的異步方法
我收到以下異常
事務已中止。嘗試提升事務時失敗。已經有一個打開的 DataReader 與此命令關聯,必須先將其關閉。等待操作超時
. 誰能告訴我我在這里做錯了什么?
namespace Playground
{
static class DalcHelper
{
public static TransactionScope GetTransactionScope()
{
return new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
}
public async static Task ReadDetails1(DependentTransaction transaction,SqlConnection conn)
{
try
{
string commandText = "SELECT * FROM dbo.Persons"; // some table, say Persons
using (SqlCommand cmd = new SqlCommand(commandText, conn))
{
cmd.CommandType = System.Data.CommandType.Text;
SqlDataReader reader = await cmd.ExecuteReaderAsync(CommandBehavior.Default);
while (reader.Read())
{
int Id = reader.GetInt32("Id");
Console.WriteLine("Id " + Id);
}
reader.Close();
}
transaction.Complete();
return;
}
catch (Exception ex)
{
Console.WriteLine("Task 1"+ ex.Message);
}
}
public async static Task ReadDetails2(DependentTransaction transaction1, SqlConnection conn)
{
try
{
string commandText = "SELECT * FROM dbo.Persons";
using (SqlCommand cmd = new SqlCommand(commandText, conn))
{
cmd.CommandType = System.Data.CommandType.Text;
SqlDataReader reader = await cmd.ExecuteReaderAsync(CommandBehavior.Default);
while (reader.Read())
{
int age = reader.GetInt32("Age");
Console.WriteLine("Age " + age);
}
reader.Close();
}
transaction1.Complete();
return;
}
catch (Exception ex)
{
Console.WriteLine("Task 2" + ex.Message);
}
}
}
class Program
{
static void Main(string[] args)
{
string connectionString = "YourConnectionString";
_ = RunMe(connectionString);
}
private async static Task RunMe(string connectionString)
{
try
{
Task task1 = Task.Run( async()=> {
using (TransactionScope scope = DalcHelper.GetTransactionScope())
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
DependentTransaction transaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
conn.Open();
await DalcHelper.ReadDetails1(transaction, conn);
/*
* add more tasks if you wish to
*/
Console.WriteLine("Completed task 1");
conn.Close();
}
scope.Complete();
}
});
Task task2 = Task.Run(async () =>
{
using (TransactionScope scope = DalcHelper.GetTransactionScope())
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
DependentTransaction transaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
conn.Open();
await DalcHelper.ReadDetails2(transaction, conn);
/*
may be update some column of table based on previous op.
// await DalcHelper.UpdateDetails2(transaction, conn);
*/
Console.WriteLine("Completed task 2");
conn.Close();
}
/*
calling `Complete` method will commit all the changes within the transaction scope(including the UpdateDetails2 method)
need not dispose transaction scope explicitly, `using` block takes care of that
*/
scope.Complete();
}
});
await Task.WhenAll(task1, task2);// at this point every task added is complete
Console.WriteLine("completed both tasks");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
使用事務 scope 時要記住的一些要點
TransactionScope
,否則可能會拋出類似Transaction already aborted
的錯誤。TransactionScope.Complete()
方法時,才會保留任何更新操作。請閱讀一些已經發布的與該主題相關的有用答案
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.