EDITED看來,使用分佈式事務(EnterpriseServicesInteropOption.Full)和長期訂戶時,該TransactionScope.Dispose方法不會等待所有提交已經完成,只是滋生的方法調用和TransactionCompleted事件後臺線程。如何確定TransactionScope已經調用了所有提交?
這是不符合的是明確規定的文件:
這種方法是同步的,會阻塞,直到事務被提交或中止。
更糟糕的是,似乎沒有辦法確定何時所有提交已處理。這是有問題的,因爲在控制檯應用程序中,主線程可以在處理後退出,並有效殺死所有後臺線程。分佈式事務中的遠程參與者永遠不會被通知這種情況,從而導致鎖保持打開,超時和其他醜陋的東西...
另一個問題是,當創建一個新的TransactionScope時,參與者仍然可以關聯與舊的交易,當他們預計將在新的登記。
下面的(簡化)代碼演示了這個問題。
我的問題:是否有人有一個想法如何確定是否安全開始一個新的循環(還)?我無法訪問Worker的代碼,所以我無法更改其中的任何內容......添加Thread.Sleep(1000)可解決問題,但會導致性能下降...
EDITED
internal class TransactionScopeTest
{
[STAThread]
public static void Main()
{
var transactionOptions = new TransactionOptions { Timeout = TransactionManager.DefaultTimeout };
var worker = new Worker();
var transactionCompletedEvent = new AutoResetEvent(true); // true to start a first loop
while (true)
{
transactionCompletedEvent.WaitOne(); // wait for previous transaction to finish
Log("Before TransactionScope");
using (var tx = new TransactionScope(TransactionScopeOption.Required, transactionOptions, EnterpriseServicesInteropOption.Full))
{
Log("Inside TransactionScope");
Transaction.Current.TransactionCompleted += delegate
{
transactionCompletedEvent.Set(); // allow a next loop to start
Log("TransactionCompleted event");
};
worker.DoWork();
Log("Before commit");
tx.Complete();
Log("Before dispose");
}
Log("After dispose");
}
}
private static void Log(string message)
{
Console.WriteLine("{0} ({1})", message, Thread.CurrentThread.ManagedThreadId);
}
public class Worker : IEnlistmentNotification
{
private Transaction _transaction;
private readonly Guid _id = Guid.NewGuid();
public void Prepare(PreparingEnlistment preparingEnlistment)
{
Log("Preparing");
preparingEnlistment.Prepared();
}
public void Commit(Enlistment enlistment)
{
Log("Committing");
_transaction = null;
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
Log("Rolling back");
_transaction = null;
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "Doubting");
_transaction = null;
enlistment.Done();
}
public void DoWork()
{
Enlist();
Log("Doing my thing...");
}
private void Enlist()
{
if (_transaction == null) //Not yet enlisted
{
Log("Enlisting in transaction");
_transaction = Transaction.Current;
_transaction.EnlistDurable(_id,this, EnlistmentOptions.EnlistDuringPrepareRequired);
return;
}
if (_transaction == Transaction.Current) //Already enlisted in current transaction
{
return;
}
throw new InvalidOperationException("Already enlisted in other transaction");
}
}
}
輸出:
Before commit (1)
Before dispose (1)
Preparing (6)
After dispose (1)
Committing (6)
TransactionCompleted event (7)
Before TransactionScope (1)
Inside TransactionScope (1)
Enlisting in transaction (1)
Doing my thing... (1)
Before commit (1)
Before dispose (1)
Preparing (7)
After dispose (1)
Before TransactionScope (1)
TransactionCompleted event (7)
Inside TransactionScope (1)
Committing (6)
Unhandled Exception: System.InvalidOperationException: Already enlisted in other transaction
你是怎麼解決這個問題的?我有同樣的問題,也使用IBM MQ。 – cudima
我添加了我實現的代碼作爲答案。這絕不是一個明確的解決方案,但它是我能找到的最好的解決方案。 –