使用整个流程的交易 [英] Using transactions across processes

查看:311
本文介绍了使用整个流程的交易的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用System.Transactions的(TransactionScope的)协调一整套流程,每个做一些数据库工作。最终,所有的流程需要提交或者通过一个父进程回滚原子。不幸的是,没有什么我试过到目前为止工作。

我的基本策略是在TransactionScope的父进程,将其保存到一个文件中,然后调用一个子进程,它加载的文件,使用事务内部自身的的TransactionScope,并返回到父。

但是,这并不为我工作。当我从调用第一个孩子回来,我有时会看到父事务已被标记为中止。尝试克隆它,然后抛出TransactionAbortedException。

我也看到了异常,当第二个孩子试图反序列化的交易,我得到code 0x8004d00e一个TransactionException。

我试图做的是在<一个描述href="http://stackoverflow.com/questions/5348242/transactionscope-across-appdomains-and-processes">TransactionScope跨应用程序域和进程和<一href="http://blogs.microsoft.co.il/blogs/sasha/archive/2010/04/30/propagating-a-transaction-across-appdomains.aspx" rel="nofollow">http://blogs.microsoft.co.il/blogs/sasha/archive/2010/04/30/propagating-a-transaction-across-appdomains.aspx 。无论如何,没有运气。

这里有一些事情我都试过了,没有成功:

  1. 从加载交易通过创建一个DependentClone DependentTransaction()的子进程
  2. 在父进程创建通过DependentClone()一DependentTransaction,保存前 交易文件
  3. 创建一个克隆()在父进程之前,保存 交易文件
  4. 使用序列保存交易
  5. 保存交易使用 TransactionInterop.GetTransactionFromTransmitterPropagationToken()
  6. 明确地打开前,家长的连接 的TransactionScope
  7. 明确地争取交易父里面
  8. 明确地争取交易中的孩子里面
  9. 在完成/未完成的范围父
  10. 在完成/未完成的范围对孩子
  11. 显式地创建一个CommittableTransaction父

下面是一个例外消息:

System.Transactions.TransactionException:本次交易已经或隐式地提交或中止。 ---> System.Runtime.InteropServices.COMException:事务已经或隐式地提交或中止(从HRESULT异常:0x8004D00E)

和其他(使用DependentClone时,()):

System.Transactions.TransactionAbortedException:该交易已中止。    在System.Transactions.TransactionStatePromotedAborted.CreateBlockingClone(在 ternalTransaction TX)    在System.Transactions.DependentTransaction..ctor(ISOLEVEL的IsolationLevel,在 ternalTransaction internalTransaction,布尔阻塞)    在System.Transactions.Transaction.DependentClone(DependentCloneOption cloneO ption)

任何想法我做错了吗?我已经尝试了很多这种排列没有任何的运气。

下面是一些code(不试图证明上述所有的变种):

  //一个变体我曾尝试是创建一个CommittableTransaction
        //并通过,在下面的范围

        使用(TransactionScope的范围=新的TransactionScope())
        {
            //可选,做一些父级EF工作

            //调用其他进程中的孩子操作
            DoChildOperation_OutOfProc(1,Transaction.Current);
            DoChildOperation_OutOfProc(2,Transaction.Current);

            scope.Complete();
        }

        //在我创建了一个CommittableTransaction的变种,
        //我在这里提交它

    ...

    私有静态无效DoChildOperation_OutOfProc(INT ID,交易成交)
    {
        字符串tranFile =的String.Format(ChildTran_ {0} .TXT,身份证);
        SaveTransactionToFile(事务,tranFile);

        工艺过程=新工艺();
        process.StartInfo =新的ProcessStartInfo(Process.GetCurrentProcess()。MainModule.FileName.Replace(vshost的String.Empty)
            的String.Format( -  CHILDID = {0} -TRANFILE = {1},身份证,tranFile));
        process.StartInfo.UseShellExecute = FALSE;
        的Process.Start();
        process.WaitForExit();
    }

    私有静态无效SaveTransactionToFile(交易成交,串tranFile)
    {
        byte []的transactionBytes =
            TransactionInterop.GetTransmitterPropagationToken(交易);

        字符串tranFileContents = Convert.ToBase64String(transactionBytes);

        File.WriteAllText(tranFile,tranFileContents);
    }

    私有静态交易LoadTransactionFromFile(字符串tranFile)
    {
        字符串tranFileContents = File.ReadAllText(tranFile);
        File.Delete(tranFile);

        byte []的tranBytes = Convert.FromBase64String(tranFileContents);

        交易TRAN =
            TransactionInterop.GetTransactionFromTransmitterPropagationToken(tranBytes);
        返回TRAN;
    }

    //应用程序的子实例的参数进行解码后,运行此
    //从DoChildOperation_OutOfProc()和装载交易出来的文件

    私有静态无效DoChildOperation(INT ID,交易childTransaction)
    {
        //在一个变体,我称之为
        // childTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete)
        //然后使用该TransactionScope的内

        使用(TransactionScope的范围=新的TransactionScope(childTransaction))
        {
            //做EF的工作和调用的SaveChanges()

            scope.Complete();
        }

        //如果我创建了一个相关的克隆,调用完成(),在这里就可以了
 

解决方案

好了,关键似乎是,你可以在父使用的TransactionScope,但不是在这个孩子。对于孩子,我打开了EF连接,调用connection.EnlistTransaction()与传递的事务,并做EF的SaveChanges()或transaction.Rollback(),但不承诺(Transaction类不提供此)。这样做,这样,看来我得到所需的行为。

在我的理解的差距确实是交易是否得到嵌套(你可以在SQL Server做的)或没有。看来,它确实不是;这是同样的交易。注意:即使您创建的孩子使用Transaction.DependentClone()一个DependentTransaction如果你把它变成一个TransactionScope,你仍然会失败

这被证明是一个有点不幸,因为这意味着,如果你的过程是家长,你可以使用的TransactionScope,但如果是一个孩子,你不能。

I'm trying to use System.Transactions (TransactionScope) to coordinate a set of processes, each of which does some database work. Ultimately all processes need to commit or be rolled back atomically via one parent process. Unfortunately, nothing I've tried so far works.

My basic strategy is to TransactionScope in the parent process, save it to a file, and invoke a child process, which loads the file, uses the transaction inside its own TransactionScope, and returns to the parent.

But this doesn't work for me. When I get back from calling the first child, I am sometimes seeing that the parent transaction has been marked as Aborted. Attempting to clone it then throws TransactionAbortedException.

I have also seen exceptions when the second child attempts to deserialize the Transaction, I get a TransactionException with code 0x8004d00e.

I am trying to do what is described in TransactionScope across AppDomains and processes , and http://blogs.microsoft.co.il/blogs/sasha/archive/2010/04/30/propagating-a-transaction-across-appdomains.aspx . Regardless, no luck.

Here are some things I have tried, without success:

  1. Creating a DependentTransaction via DependentClone() in the child process from the loaded transaction
  2. Creating a DependentTransaction via DependentClone() in the parent process, before saving the transaction to a file
  3. Creating a Clone() in the parent process, before saving the transaction to a file
  4. Saving the transaction using serialization
  5. Saving the transaction using TransactionInterop.GetTransactionFromTransmitterPropagationToken()
  6. Explicitly opening the connection before the parent's TransactionScope
  7. Explicitly enlisting the transaction inside the parent
  8. Explicitly enlisting the transaction inside the child
  9. Completing/not completing the scope on the parent
  10. Completing/not completing the scope on the child
  11. Explicitly creating a CommittableTransaction in the parent

Here's one exception message:

System.Transactions.TransactionException: The transaction has already been implicitly or explicitly committed or aborted. ---> System.Runtime.InteropServices.COMException: The transaction has already been implicitly or explicitly committed or aborted (Exception from HRESULT: 0x8004D00E)

And the other (when using DependentClone()):

System.Transactions.TransactionAbortedException: The transaction has aborted. at System.Transactions.TransactionStatePromotedAborted.CreateBlockingClone(In ternalTransaction tx) at System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, In ternalTransaction internalTransaction, Boolean blocking) at System.Transactions.Transaction.DependentClone(DependentCloneOption cloneO ption)

Any ideas what I'm doing wrong? I've tried a lot of permutations of this without any luck.

Here is some code (which doesn't attempt to demonstrate all the variants described above):

        // 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

解决方案

Okay, the key seems to be that you can use TransactionScope in the parent, but not in the child. For the child, I open the EF connection, call connection.EnlistTransaction() with the passed transaction, and do EF SaveChanges() or transaction.Rollback() but not commit (the Transaction class does not offer this). Doing it this way, it appears that I get the desired behavior.

The gap in my understanding was really whether the transaction was getting nested (as you can do in SQL Server) or not. It appears that it really isn't; it's the same transaction. Note: Even if you create a DependentTransaction using Transaction.DependentClone() in the child, you will still fail if you put it into a TransactionScope.

This proves to be a little unfortunate, because it means that if your process is the parent, you can use TransactionScope, but if it is a child, you cannot.

这篇关于使用整个流程的交易的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆