为了处理SQL超时,我试图使用SqlAzureExecutionStrategy(https://msdn.microsoft.com/en-us/data/dn456835.aspx)
我遇到的问题是它阻止了“用户启动的事务”,这似乎是在EF(http://www.hanselman.com/blog/GettingLINQToSQLAndLINQToEntitiesToUseNOLOCK.aspx,NOLOCK with Linq to SQL)中实现“ with(nolock)”的推荐方法。
示例代码
public AspnetUser GetAspnetUserByUserName(string userName)
{
using (var tx = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }))
{
return context.AspnetUsers.Where(x => x.UserName == userName).FirstOrDefault();
}
}
引发错误
配置的执行策略“ SqlAzureExecutionStrategy”不支持用户启动的事务。有关其他信息,请参见http://go.microsoft.com/fwlink/?LinkId=309381。
我已经看到了回答,说每个呼叫都关闭SqlAzureExecutionStrategy的答案,但是如果我所有的阅读都忽略了该策略,那将无法使用它。可能同时具有“ NoLock”和SqlAzureExecutionStrategy
最佳答案
SqlAzureExecutionStrategy不支持在要重试的操作之外发起的事务。要解决此限制,您将需要暂停该策略,创建事务作用域并将其作为您手动传递给要重试的执行策略的操作来进行:
public AspnetUser GetAspnetUserByUserName(string userName)
{
new SuspendableSqlAzureExecutionStrategy().Execute(() =>
{
using (var tx = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }))
{
return context.AspnetUsers.Where(x => x.UserName == userName).FirstOrDefault();
}
});
}
在这里,我使用了https://msdn.microsoft.com/en-us/data/dn307226中可暂停策略的替代方法,该方法将自动暂停任何嵌套的调用:
using System.Data.Entity.Infrastructure;
using System.Data.Entity.SqlServer;
using System.Data.Entity.Utilities;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Threading.Tasks;
public class SuspendableSqlAzureExecutionStrategy : IDbExecutionStrategy
{
private readonly IDbExecutionStrategy _azureExecutionStrategy;
public SuspendableSqlAzureExecutionStrategy()
{
_azureExecutionStrategy = new SqlAzureExecutionStrategy();
}
private static bool Suspend
{
get { return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false; }
set { CallContext.LogicalSetData("SuspendExecutionStrategy", value); }
}
public bool RetriesOnFailure
{
get { return !Suspend; }
}
public virtual void Execute(Action operation)
{
if (!RetriesOnFailure)
{
operation();
return;
}
try
{
Suspend = true;
_azureExecutionStrategy.Execute(operation);
}
finally
{
Suspend = false;
}
}
public virtual TResult Execute<TResult>(Func<TResult> operation)
{
if (!RetriesOnFailure)
{
return operation();
}
try
{
Suspend = true;
return _azureExecutionStrategy.Execute(operation);
}
finally
{
Suspend = false;
}
}
public virtual async Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken)
{
if (!RetriesOnFailure)
{
await operation();
return;
}
try
{
Suspend = true;
await _azureExecutionStrategy.ExecuteAsync(operation, cancellationToken);
}
finally
{
Suspend = false;
}
}
public virtual async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken)
{
if (!RetriesOnFailure)
{
return await operation();
}
try
{
Suspend = true;
return await _azureExecutionStrategy.ExecuteAsync(operation, cancellationToken);
}
finally
{
Suspend = false;
}
}
}
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SuspendableSqlAzureExecutionStrategy());
}
}