【问题标题】:How to use SqlAzureExecutionStrategy and "Nolock"如何使用 SqlAzureExecutionStrategy 和“Nolock”
【发布时间】:2015-08-08 22:55:40
【问题描述】:

为了处理 SQL 超时,我尝试使用 SqlAzureExecutionStrategy (https://msdn.microsoft.com/en-us/data/dn456835.aspx)

我遇到的问题是它阻止了“用户启动的事务”,这似乎是在 EF(http://www.hanselman.com/blog/GettingLINQToSQLAndLINQToEntitiesToUseNOLOCK.aspxNOLOCK 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

【问题讨论】:

    标签: linq entity-framework azure entity-framework-6 azure-sql-database


    【解决方案1】:

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

    【讨论】:

    • 我会尝试测试这个,这周让你知道它是如何工作的,谢谢
    • 唯一我不清楚的是为什么暂停必须公开。现在也试试看,似乎比 MSDN 版本更干净。
    • 工作得很好。将挂起更新为对我来说是私有的,但这是一个非常好的实现,谢谢。
    • @RobertNoack 你有机会分享你的代码吗?
    • @AndriySvyryd “检查”的名称和类型是什么?
    猜你喜欢
    • 2020-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 2014-02-02
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    相关资源
    最近更新 更多