C# 跨流程使用事务

C# 跨流程使用事务,c#,.net,transactionscope,msdtc,system.transactions,C#,.net,Transactionscope,Msdtc,System.transactions,我试图使用System.Transactions(TransactionScope)来协调一组进程,每个进程都做一些数据库工作。最终,所有进程都需要通过一个父进程以原子方式提交或回滚。不幸的是,到目前为止我试过的都不管用 我的基本策略是在父进程中使用TransactionScope,将其保存到文件中,然后调用子进程,该子进程加载文件,在其自己的TransactionScope中使用事务,然后返回父进程 但这对我不起作用。当我从调用第一个子事务返回时,有时会看到父事务被标记为已中止。尝试克隆它会引

我试图使用System.Transactions(TransactionScope)来协调一组进程,每个进程都做一些数据库工作。最终,所有进程都需要通过一个父进程以原子方式提交或回滚。不幸的是,到目前为止我试过的都不管用

我的基本策略是在父进程中使用TransactionScope,将其保存到文件中,然后调用子进程,该子进程加载文件,在其自己的TransactionScope中使用事务,然后返回父进程

但这对我不起作用。当我从调用第一个子事务返回时,有时会看到父事务被标记为已中止。尝试克隆它会引发TransactionBortedException

我还看到了一些异常,当第二个子项尝试反序列化事务时,我得到了一个代码为0x8004d00e的TransactionException

我正在尝试执行和中描述的操作。不管怎样,没有运气

以下是我尝试过但没有成功的一些事情:

  • 从加载的事务在子进程中通过DependentClone()创建DependentTransaction
  • 在保存之前,通过父进程中的DependentClone()创建DependentTransaction 事务到文件
  • 在父进程中创建克隆(),然后保存 事务到文件
  • 使用序列化保存事务
  • 使用保存事务 TransactionInterop.GetTransactionFromTransmitterPropagationToken()
  • 在父连接之前显式打开连接 交易镜
  • 在父级内显式登记事务
  • 在子系统中显式登记事务
  • 正在完成/未完成父级上的范围
  • 正在完成/未完成子对象上的范围
  • 在父级中显式创建CommittableTransaction
  • 这里有一条例外消息:

    System.Transactions.TransactionException:事务已隐式或显式提交或中止。-->System.Runtime.InteropServices.ComeException:事务已隐式或显式提交或中止(HRESULT的异常:0x8004D00E)

    另一个(使用DependentClone()时):

    System.Transactions.TransactionBortedException:事务已中止。 在System.Transactions.TransactionStatePromotedAborted.CreateBlockingClone(在 ternalTransaction(德克萨斯州) 在System.Transactions.DependentTransaction..ctor(隔离级别,在 ternalTransaction(内部事务,布尔阻塞) 在System.Transactions.Transaction.DependentClone(DependentClone选项 (续)

    知道我做错了什么吗?我已经尝试了很多这种排列方式,但没有任何运气

    下面是一些代码(不尝试演示上述所有变体):


    好的,关键似乎是您可以在父级中使用TransactionScope,但不能在子级中使用。对于子级,我打开EF连接,使用传递的事务调用connection.EnstractTransaction(),然后执行EF SaveChanges()或transaction.Rollback(),但不执行commit(事务类不提供此功能)。这样做,似乎我得到了想要的行为

    我在理解上的差距实际上是事务是否嵌套(在SQL Server中可以这样做)。看来事实并非如此;这是同一笔交易。注意:即使在子对象中使用Transaction.DependentClone()创建DependentTransaction,如果将其放入TransactionScope中,仍将失败


    这证明有点不幸,因为这意味着如果您的流程是父流程,您可以使用TransactionScope,但如果它是子流程,您就不能使用它。

    您已经解释了您遇到的问题/错误,这真是太好了。。但是,你能不能显示执行事务代码的完整代码块…?我添加了一些代码,经过一些清理,但希望它能让它更清晰。这看起来有点毛茸茸的。为什么不能将工作拆分为多个事务,然后在必要时使用补偿回滚>最终目标是能够调用其他服务来分发工作。每个服务只应根据提供给它的信息(可能包括也可能不包括事务)了解其工作。如果数据部分提交,那么数据库将处于损坏状态,即使我可以实现补偿逻辑。此外,补偿逻辑可能会增加额外的状态,而且肯定会增加很多额外的复杂性。我引用的链接上的信息显示,你可以像我一样传递交易,所以这不仅仅适用于我,这让我感到困惑。我也有类似的问题,但这是因为我正在卸载应用程序域。在提交父事务之前,我必须保持应用程序域的活动状态,然后卸载应用程序域。-->
            // 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