问题描述
我在需要使用分布式事务的地方实现了 EF 6 执行策略的暂停,以避免出现'SqlAzureExecutionStrategy' 不支持用户发起的事务"异常,遵循以下示例:
I implemented suspension of the EF 6 execution strategy wherever I need to use distributed transaction to avoid "'SqlAzureExecutionStrategy' does not support user initiated transactions" exceptions, following these examples:
https://romiller.com/2013/08/19/ef6-suspendable-execution-strategy/https://msdn.microsoft.com/en-us/library/dn307226(v=vs.113).aspx
但是,最近我在批量数据管理会话期间遇到了两次失败,最终导致出现上述异常.
However, recently I got two failures of this during a bulk data management session, resulting in getting the above exception after all.
如果我理解正确,给定的示例在全局级别启用/禁用执行策略,这意味着如果在多个线程上同时执行操作,则一个操作可以在另一个操作完成之前结束暂停.如果使用 .NET 4.6.1 处理跨多个 SQL Azure DB 的事务(这可能需要一段时间才能完成),效果可能最为明显.
If I understand it correctly, the given examples enable/disable the execution strategy on a global level, which would mean that if actions are executed simultaneously on multiple threads one action can end the suspension before another one is completed. The effect is probably most noticeable if using .NET 4.6.1 for transactions spanning multiple SQL Azure DBs, which can take a while to complete.
为了避免这种情况,我求助于创建一个全局事务计数器,它以线程安全的方式递增和递减,并且只有在没有待处理的事务时才解除暂停,例如:
To avoid this I resorted to creating a global transaction counter, which is incremented and decremented in a thread safe way, and lifting the suspension only if there are no transactions pending anymore, like:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
this.SetExecutionStrategy("System.Data.SqlClient", () => SuspendExecutionStrategy
? (IDbExecutionStrategy)new DefaultExecutionStrategy()
: new SqlAzureExecutionStrategy());
}
public static bool SuspendExecutionStrategy
{
get
{
return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false;
}
set
{
CallContext.LogicalSetData("SuspendExecutionStrategy", value);
}
}
}
然后:
public class ExecutionHelper
{
private static int _pendingTransactions;
public void ExecuteUsingTransaction(Action action)
{
SuspendExeutionStrategy();
try
{
using (var transaction = new TransactionScope())
{
action();
transaction.Complete();
}
ResetSuspension();
}
catch (Exception ex)
{
ResetSuspension();
throw ex;
}
}
private void SuspendExeutionStrategy()
{
Interlocked.Increment(ref _pendingTransactions);
MyConfiguration.SuspendExecutionStrategy = true;
}
private void ResetSuspension()
{
Interlocked.Decrement(ref _pendingTransactions);
if (_pendingTransactions < 1 )
{
MyConfiguration.SuspendExecutionStrategy = false;
}
}
}
尽管 MSDN 上给出的示例没有考虑到这一点,但我仍然感到困惑.有什么我忽略的吗?
I am still puzzled though that the example given on MSDN does not take this into account. Is there something I overlooked?
推荐答案
更新:
事实证明 CallContext 无论如何都是特定于线程的,因此在这种情况下您不必担心多线程.(见备注:https://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext(v=vs.110).aspx)
Turns out the CallContext is thread specific anyway, so you don't have to worry about multi-threading in this scenario anyway. (see remarks: https://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext(v=vs.110).aspx)
在我尝试获得与工作类似的东西时遇到了这个问题.
Came across this whilst i was trying to get something similar to work.
很确定您的解决方案实际上不是线程安全的.一个线程仍然可以进入并将暂停设置为真,在重置减少数字和将暂停设置为 false 之间,这意味着新线程不会将暂停设置为 true 并且会失败.
Pretty sure your solution there isn't actually thread safe. A thread can still come in and set the suspension to true between the reset decrementing the number and setting the suspension to false, meaning the new thread wouldn't have the suspension set to true and would fail.
您将需要一个 ReaderWriterLock 来确保您阻止了任何将暂停设置为 true 的新线程,同时您在重置方法中减少、检查和假"了暂停.喜欢:
You would need a ReaderWriterLock to ensure you blocked any new threads setting the suspension to true whilst you decremented, checked and "falsed" the suspension in the reset method. Like:
private ReaderWriterLock _readerWriterLock = new ReaderWriterLock();
private void SuspendExeutionStrategy()
{
_readerWriterLock.AcquireReaderLock(_timeout);
Interlocked.Increment(ref _pendingTransactions);
MyConfiguration.SuspendExecutionStrategy = true;
_readerWriterLock.ReleaseReaderLock();
}
private void ResetSuspension()
{
_readerWriterLock.AcquireWriterLock(_timeout);
Interlocked.Decrement(ref _pendingTransactions);
if (_pendingTransactions < 1)
{
MyConfiguration.SuspendExecutionStrategy = false;
}
_readerWriterLock.ReleaseWriterLock();
}
这篇关于EF 6 暂停执行策略和重叠执行 - “不支持用户发起的事务"例外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!