TransactionScope 在释放时抛出 TransactionAbortedException
本文关键字:TransactionAbortedException 释放 TransactionScope | 更新日期: 2023-09-27 18:34:00
我有以下情况:
- 父进程启动 TransactionScope,使用 TransactionInterop.GetTransmitterPropagationToken 创建标识事务的令牌,将数据插入数据库。事务范围完成。
- 启动另一个进程,使用上面提到的令牌创建事务,然后用于创建事务范围。此过程还会将数据插入数据库。事务范围完成并被释放。
- 此时,父进程尝试释放其事务范围,并引发事务中止异常。该异常不会给出无法提交的任何原因。
堆栈跟踪:
at System.Transactions.TransactionStatePromotedAborted.BeginCommit(InternalTransaction tx, Boolean asyncCommit, AsyncCallback asyncCallback, Object asyncState)
at System.Transactions.CommittableTransaction.Commit()
at System.Transactions.TransactionScope.InternalDispose()
at System.Transactions.TransactionScope.Dispose()
at DistributedTransactions.Program.Main() in c:'Users'agolan.ALLSHARE'Documents'Visual Studio 2013'Projects'DistributedTransactions'DistributedTransactions'Program.cs:line 44
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
以下是应用程序的代码:
internal class Program {
private static void Main() {
string connString = "data source=.;initial catalog=Test;integrated security=True;persist security info=True";
string tokenFile = @"c:'Temp'token.txt";
Transaction transaction = null;
bool isChild = false;
if (File.Exists(tokenFile)) {
isChild = true;
string tokenString = File.ReadAllText(tokenFile);
byte[] token = Convert.FromBase64String(tokenString);
transaction = TransactionInterop.GetTransactionFromTransmitterPropagationToken(token);
}
using (var transactionScope = transaction != null ? new TransactionScope(transaction) : new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 15, 0))) {
var curr = Transaction.Current;
if (!isChild) {
byte[] transactionBytes = TransactionInterop.GetTransmitterPropagationToken(curr);
string tokenString = Convert.ToBase64String(transactionBytes);
File.WriteAllText(tokenFile, tokenString);
}
using (var conn = new SqlConnection(connString)) {
conn.Open();
using (SqlCommand cmd = conn.CreateCommand()) {
Console.WriteLine("Enter id and value");
cmd.CommandText = "INSERT INTO KeyValue(Id, Value) VALUES (@1, @2)";
cmd.Parameters.Add(new SqlParameter("@1", Console.ReadLine()));
cmd.Parameters.Add(new SqlParameter("@2", Console.ReadLine()));
cmd.ExecuteNonQuery();
}
}
transactionScope.Complete();
Console.WriteLine("Dispose");
Console.ReadLine();
}
}
}
问题:
- 为什么它不能提交? 在这种情况下是否可以使用事务范围
- (我阅读了答案跨进程使用事务,但我想使用事务范围(?如何?
我能够在本地复制它,这是因为另一个应用程序在父进程提交事务之前退出了。其他应用程序必须继续运行,直到父应用程序提交或回滚事务。执行此操作的一种方法是防止应用程序退出,直到触发 TransactionCompleted 事件。
我修改了您的代码以使用 ManualResetEventSlim 使子进程等待父事务进程完成,然后再退出。
internal class Program
{
private static void Main()
{
string connString = "data source=.;initial catalog=Test;integrated security=True;persist security info=True";
string tokenFile = @"c:'Temp'token.txt";
Transaction transaction = null;
bool isChild = false;
if (File.Exists(tokenFile))
{
isChild = true;
string tokenString = File.ReadAllText(tokenFile);
byte[] token = Convert.FromBase64String(tokenString);
transaction = TransactionInterop.GetTransactionFromTransmitterPropagationToken(token);
}
using (var parentTxCompleteEvent = new ManualResetEventSlim(!isChild))
{
using (var transactionScope = transaction != null ? new TransactionScope(transaction) : new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 15, 0)))
{
var curr = Transaction.Current;
if (!isChild)
{
byte[] transactionBytes = TransactionInterop.GetTransmitterPropagationToken(curr);
string tokenString = Convert.ToBase64String(transactionBytes);
File.WriteAllText(tokenFile, tokenString);
}
else
{
transaction.TransactionCompleted += (sender, e) => parentTxCompleteEvent.Set();
}
using (var conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
Console.WriteLine("Enter id and value");
cmd.CommandText = "INSERT INTO KeyValue(Id, Value) VALUES (@1, @2)";
cmd.Parameters.Add(new SqlParameter("@1", Console.ReadLine()));
cmd.Parameters.Add(new SqlParameter("@2", Console.ReadLine()));
cmd.ExecuteNonQuery();
}
}
transactionScope.Complete();
Console.WriteLine("Dispose");
Console.ReadLine();
}
parentTxCompleteEvent.Wait();
}
}
}