Каков наилучший способ узнать, находится ли блок кода внутри TransactionScope?
Является ли Transaction.Current реальным способом сделать это или есть какие-то тонкости?
Возможно ли получить доступ к внутреннему ContextData.CurrentData.CurrentScope(в System.Transactions) с отражением? Если да, то как?
Как узнать, находится ли код внутри TransactionScope?
Ответ 1
Вот более надежный способ (как я уже сказал, Transaction.Current можно установить вручную, и это не всегда означает, что мы действительно находимся в TransactionScope). Также можно получить эту информацию с отражением, но излучение IL работает в 100 раз быстрее, чем отражение.
private Func<TransactionScope> _getCurrentScopeDelegate;
bool IsInsideTransactionScope
{
get
{
if (_getCurrentScopeDelegate == null)
{
_getCurrentScopeDelegate = CreateGetCurrentScopeDelegate();
}
TransactionScope ts = _getCurrentScopeDelegate();
return ts != null;
}
}
private Func<TransactionScope> CreateGetCurrentScopeDelegate()
{
DynamicMethod getCurrentScopeDM = new DynamicMethod(
"GetCurrentScope",
typeof(TransactionScope),
null,
this.GetType(),
true);
Type t = typeof(Transaction).Assembly.GetType("System.Transactions.ContextData");
MethodInfo getCurrentContextDataMI = t.GetProperty(
"CurrentData",
BindingFlags.NonPublic | BindingFlags.Static)
.GetGetMethod(true);
FieldInfo currentScopeFI = t.GetField("CurrentScope", BindingFlags.NonPublic | BindingFlags.Instance);
ILGenerator gen = getCurrentScopeDM.GetILGenerator();
gen.Emit(OpCodes.Call, getCurrentContextDataMI);
gen.Emit(OpCodes.Ldfld, currentScopeFI);
gen.Emit(OpCodes.Ret);
return (Func<TransactionScope>)getCurrentScopeDM.CreateDelegate(typeof(Func<TransactionScope>));
}
[Test]
public void IsInsideTransactionScopeTest()
{
Assert.IsFalse(IsInsideTransactionScope);
using (new TransactionScope())
{
Assert.IsTrue(IsInsideTransactionScope);
}
Assert.IsFalse(IsInsideTransactionScope);
}
Ответ 2
Transaction.Current
должен быть надежным; Я только что проверил, при этом отлично работает и с подавленными транзакциями:
Console.WriteLine(Transaction.Current != null); // false
using (TransactionScope tran = new TransactionScope())
{
Console.WriteLine(Transaction.Current != null); // true
using (TransactionScope tran2 = new TransactionScope(
TransactionScopeOption.Suppress))
{
Console.WriteLine(Transaction.Current != null); // false
}
Console.WriteLine(Transaction.Current != null); // true
}
Console.WriteLine(Transaction.Current != null); // false