7

为了对付SQL超时,我试图用SqlAzureExecutionStrategy(https://msdn.microsoft.com/en-us/data/dn456835.aspx如何使用SqlAzureExecutionStrategy和“NOLOCK”

我遇到的问题是它可以防止“用户发起的交易”,这似乎是推荐在EF(http://www.hanselman.com/blog/GettingLINQToSQLAndLINQToEntitiesToUseNOLOCK.aspx,NOLOCK with Linq to SQL)中实施“(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

回答

11

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()); 
    } 
} 
+0

我会尝试进行测试,这一周,让你知道它是如何工作的感谢 –

+0

只有我不清楚关于这就是为什么事情暂停必须是公开的。现在给这个试试看,似乎比MSDN版本更清洁。 –

+0

工作得很好。更新暂停是我的私人,但这是一个非常好的实现,谢谢你。 –

相关问题