我正在尝试使用 System.Transactions (TransactionScope) 来协调一组进程,每个进程都执行一些数据库工作。最终,所有进程都需要通过一个父进程以原子方式提交或回滚。不幸的是,到目前为止我所尝试的一切都不起作用。
我的基本策略是在父进程中使用 TransactionScope,将其保存到文件中,然后调用子进程,该子进程加载该文件,在其自己的 TransactionScope 内使用事务,然后返回到父进程。
但这对我不起作用。当我从调用第一个子事务回来时,有时会看到父事务已被标记为“已中止”。尝试克隆它会抛出 TransactionAbortedException。
当第二个孩子尝试反序列化事务时,我也看到了异常,我收到代码为 0x8004d00e 的 TransactionException。
我正在尝试做中描述的事情跨 AppDomains 和进程的 TransactionScope https://stackoverflow.com/questions/5348242/transactionscope-across-appdomains-and-processes , and http://blogs.microsoft.co.il/blogs/sasha/archive/2010/04/30/propagating-a-transaction-across-appdomains.aspx http://blogs.microsoft.co.il/blogs/sasha/archive/2010/04/30/propagating-a-transaction-across-appdomains.aspx。无论如何,没有运气。
以下是我尝试过但没有成功的一些事情:
- 在子进程中从加载的事务中通过 DependentClone() 创建 DependentTransaction
- 在保存之前,在父进程中通过 DependentClone() 创建 DependentTransaction
事务到文件
- 在保存之前,在父进程中创建 Clone()
事务到文件
- 使用序列化保存事务
- 使用保存交易
TransactionInterop.GetTransactionFromTransmitterPropagationToken()
- 在父级之前显式打开连接
交易范围
- 在父级中显式登记事务
- 在子进程中显式登记交易
- 完成/未完成父级的范围
- 完成/未完成子项的范围
- 在父级中显式创建 CommittableTransaction
这是一条异常消息:
System.Transactions.TransactionException:事务已隐式或显式提交或中止。 ---> System.Runtime.InteropServices.COMException:事务已隐式或显式提交或中止(来自 HRESULT 的异常:0x8004D00E)
另一个(使用 DependentClone() 时):
System.Transactions.TransactionAbortedException:事务已中止。
在 System.Transactions.TransactionStatePromotedAborted.CreateBlockingClone (在
外部交易 tx)
在 System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel,在
ternalTransaction 内部事务,布尔阻塞)
在System.Transactions.Transaction.DependentClone(DependentCloneOption克隆O
选项)
有什么想法我做错了吗?我已经尝试了很多这种排列但没有任何运气。
这是一些代码(并不试图演示上述所有变体):
// one variant I have tried is to create a CommittableTransaction
// and pass that in the scope below
using (TransactionScope scope = new TransactionScope())
{
// optionally, do some parent-level EF work
// invoke child operations in other processes
DoChildOperation_OutOfProc(1, Transaction.Current);
DoChildOperation_OutOfProc(2, Transaction.Current);
scope.Complete();
}
// in the variant where I created a CommittableTransaction,
// I committed it here
...
private static void DoChildOperation_OutOfProc(int id, Transaction transaction)
{
string tranFile = string.Format("ChildTran_{0}.txt", id);
SaveTransactionToFile(transaction, tranFile);
Process process = new Process();
process.StartInfo = new ProcessStartInfo(Process.GetCurrentProcess().MainModule.FileName.Replace(".vshost", string.Empty),
string.Format("-CHILDID={0} -TRANFILE={1}", id, tranFile));
process.StartInfo.UseShellExecute = false;
process.Start();
process.WaitForExit();
}
private static void SaveTransactionToFile(Transaction transaction, string tranFile)
{
byte[] transactionBytes =
TransactionInterop.GetTransmitterPropagationToken(transaction);
string tranFileContents = Convert.ToBase64String(transactionBytes);
File.WriteAllText(tranFile, tranFileContents);
}
private static Transaction LoadTransactionFromFile(string tranFile)
{
string tranFileContents = File.ReadAllText(tranFile);
File.Delete(tranFile);
byte[] tranBytes = Convert.FromBase64String(tranFileContents);
Transaction tran =
TransactionInterop.GetTransactionFromTransmitterPropagationToken(tranBytes);
return tran;
}
// the child instance of the app runs this after decoding the arguments
// from DoChildOperation_OutOfProc() and loading the transaction out of the file
private static void DoChildOperation(int id, Transaction childTransaction)
{
// in one variant, I call
// childTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete)
// and then use that inside the TransactionScope
using (TransactionScope scope = new TransactionScope(childTransaction))
{
// do EF work and call SaveChanges()
scope.Complete();
}
// if I created a dependent clone, call Complete() here on it