当使用[Transaction(TransactionScopeOption.RequiresNew)]时,Castle.Facilities.AutoTx不会启动新的可提交事务

时间:2014-08-05 16:32:14

标签: castle-windsor castle-autotx

使用带有Castle.Facilities.AutoTx属性的[Transaction(TransactionScopeOption.RequiresNew)]工具时,不会创建预期的新System.Transactions.CommittableTransaction

您可以使用以下单元测试轻松测试

using System.Transactions;
using Castle.Facilities.AutoTx.Testing;
using Castle.MicroKernel.Registration;
using Castle.Transactions;
using Castle.Windsor;
using NUnit.Framework;

namespace Castle.Facilities.AutoTx.Tests
{
    public class TransService
    {
        private readonly NewTransService _s2;

        public TransService(NewTransService s2)
        {
            _s2 = s2;
        }

        [Transaction]
        public virtual string DoInTrans()
        {
            var currentTransaction = System.Transactions.Transaction.Current;
            Assert.That(currentTransaction != null, "The current transaction mustn't be null.");
            string transId = currentTransaction.TransactionInformation.LocalIdentifier;

            _s2.DoInNewTrans(transId);

            return transId;
        }
    }

    public class NewTransService
    {
        [Transaction(TransactionScopeOption.RequiresNew)]
        public virtual string DoInNewTrans(string parentTransId)
        {
            var currentTransaction = System.Transactions.Transaction.Current;
            Assert.That(currentTransaction != null, "The current transaction mustn't be null.");
            string transId = currentTransaction.TransactionInformation.LocalIdentifier;

            Assert.AreNotEqual(parentTransId, transId, "Ambient transaction must differ from parent");

            return transId;
        }
    }

    public class SingleThread_NewAmbient
    {
        private WindsorContainer _Container;

        [SetUp]
        public void SetUp()
        {
            _Container = new WindsorContainer();
            _Container.AddFacility<AutoTxFacility>();
            _Container.Register(Component.For<TransService>());
            _Container.Register(Component.For<NewTransService>());
        }

        [TearDown]
        public void TearDown()
        {
            _Container.Dispose();
        }

        [Test]
        public void Automatically_Starts_New_CommitableTransaction()
        {
            using (var scope = new ResolveScope<TransService>(_Container))
                scope.Service.DoInTrans();
        }

    }
}

我误解了[Transaction(TransactionScopeOption.RequiresNew)]的目的还是错误?

我一直在深入研究Castle.Transactions源代码,我可以通过更改Castle.Transactions.TransactionManager.ITransactionManager.CreateTransaction(ITransactionOptions transactionOptions)中的以下代码来修复行为:

if (activity.Count == 0)
    tx = new Transaction(new CommittableTransaction(new TransactionOptions
    ...

if (activity.Count == 0 || transactionOptions.Mode == TransactionScopeOption.RequiresNew)
    tx = new Transaction(new CommittableTransaction(new TransactionOptions
    ...

来自Castle专家/所有者的人可以检查一下吗?

1 个答案:

答案 0 :(得分:2)

作者在这里,

我认为你的代码很棒,并且如果它没有打破其他测试,它会合并PR。 =)

RequiresNew不受支持的原因是因为它在99%的情况下都是反模式。在将您的工作单元封装在交易中之后,您就可以了;并且您的工作单元应与业务操作1-1相对应。

现在,如果您在当前主题上进行了交易,就像您在需要使用“需要新内容”的情况下一样,那么您在阅读脏话之后数据或产生不相关的事务(从业务操作的角度来看)。因此,你应该在另一个线程中这样做。

由于交易是“环境问题”。并且在C#这样的编程语言的控制流程中没有明确表示,你留下了你的调用上下文插槽&#39;保存交易参考;但从你的代码的角度来看,这些并不存在;你所拥有的是仅在事务上下文中工作的部分函数。如果你产生第二个交易;你打算如何与当前交易协调?这很难;并可能导致问题。

一边

在其他交易系统中,例如geteventstore.com,您会获得一个显式的事务标识符 - 您在System.Transactions中也有一个,但它在API / ABI / ADO.Net中没有明确的数据库因此你不能以同样的方式使用它。使用明确的交易标识符,您可以在遇到疑问时解决失败,即“将军问题”。你不能使用System.Transactions。相反,您必须在有问题的DTC上安装事务MMC并手动回滚或转发。