Castle.Facilities.AutoTx не запускает новую обязательную транзакцию, когда используется [Transaction(TransactionScopeOption.RequiresNew)]
Когда используешь 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
...
Может кто-нибудь из экспертов / владельцев Замка проверить это?
1 ответ
Автор здесь,
Я думаю, что ваш код великолепен и слил бы PR с ним, если бы он не нарушал другие тесты. знак равно
Причина, по которой RequNNew не очень хорошо поддерживается, заключается в том, что в 99% случаев это анти-шаблон. Вы после инкапсуляции вашей единицы работы в транзакции; и ваша единица работы должна соответствовать 1-1 бизнес-операции, которая должна быть последовательной.
Теперь, если у вас есть транзакция, идущая в текущем потоке, как в вашем случае, если вам нужно использовать "RequNew", тогда вы либо читаете грязные данные, либо порождаете несвязанную транзакцию (из бизнес-операции). перспективы). Следовательно, вы должны делать это в другой теме.
Поскольку транзакции являются "окружающими" и не явными в потоке управления на языке программирования, таком как C#, у вас остаются "слоты контекста вызова" для сохранения ссылки на транзакцию; но с точки зрения вашего кода они не существуют; у вас есть частичные функции, которые работают только в транзакционном контексте. Если вы породили вторую транзакцию; как вы собираетесь согласовать это с текущей транзакцией? Это тяжело; и может привести к проблемам.
В сторону
В других транзакционных системах, таких как geteventstore.com, вы получаете явный идентификатор транзакции - он также есть в System.Transactions, но он не является явным в API/ABI/ADO.Net для базы данных, поэтому вы не можете использовать это так же. С помощью явного идентификатора транзакции вы можете устранять ошибки, когда они становятся "сомнительными", то есть "общей проблемой"; вы не можете с System.Transactions. Вместо этого необходимо подключить транзакцию MMC к соответствующему DTC и вручную откатить ее назад или вперед.