EF死锁处理之:给 EF Core 查询增加 With NoLock

EF版本6.x.x

在项目里添加一个拦截器Interceptor

public class WithNoLockInterceptor: DbCommandInterceptor
    {
        private static readonly Regex TableAliasRegex =
            new Regex(@"(?<tableAlias>(FROM|JOIN) \[[a-zA-Z]\w*\] AS \[[a-zA-Z]\w*\](?! WITH \(NOLOCK\)))",
                RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
        [ThreadStatic]
        public static bool WithNolock = false;
        public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
        {
            if (WithNolock)
            {
                command.CommandText = TableAliasRegex.Replace(
                command.CommandText,
                "${tableAlias} WITH (NOLOCK)"
                );
            }
            return base.ScalarExecuting(command, eventData, result);
        }

        public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            if (WithNolock)
            {
                command.CommandText = TableAliasRegex.Replace(
                    command.CommandText,
                    "${tableAlias} WITH (NOLOCK)"
                    );
            }
            return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
        }

        public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
        {
            if (WithNolock)
            {
                command.CommandText = TableAliasRegex.Replace(
                command.CommandText,
                "${tableAlias} WITH (NOLOCK)"
                );
            }
            return result;
        }

        public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            if (WithNolock)
            {
                command.CommandText = TableAliasRegex.Replace(
                    command.CommandText,
                    "${tableAlias} WITH (NOLOCK)"
                    );
            }
            return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
        }
    }

 

添加一个NoLock的静态类

public static class WithLockUtility 
    {
        /// <summary>
        /// 部分查询不使用锁查询的,可以调用此扩展(默认全局查询不使用with(nolock))
        /// 参考:https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1637/
        /// 示例:
        /// 1、query.OrderByCustom(filters.orderFields).Select({...}).NoLocking(querable => querable.PagingAsync(filters.page, filters.rows));
        /// 2、repository.EntitiesAsNoTracking.Select(...).NoLocking(item=>item.FirstOrDefaultAsync());
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="query"></param>
        /// <param name="queryAction"></param>
        /// <returns></returns>
        public static TResult NoLocking<T, TResult>(this IQueryable<T> query, Func<IQueryable<T>, TResult> queryAction)
        {
            WithNoLockInterceptor.WithNolock = true;

            TResult queryableResult = default(TResult);
            try
            {
                queryableResult = queryAction(query);
            }
            finally
            {
                WithNoLockInterceptor.WithNolock = false;
            }
            return queryableResult;
        }
    }

在dbcontext引用的地方引用拦截器

services.AddDbContext<Context>(options =>  
            options.UseSqlServer(connectionString)
            .AddInterceptors(new WithNoLockInterceptor())
            );

 

使用NoLocking示例

_dbContext.IntegrationLogs
                  .Where(log => log.LogId == request.LogId)
                  .Select(log => new IntegrationLogDto
                  {
                      Id = log.LogId,
                      Level = log.Level,
                      Params = log.Params,
                      Message = log.Message,
                      CreatedOn = log.CreatedOn
                  })
                  .Nolocking(querable => querable.ToList());

 

 

参考地址:

http://www.manongjc.com/detail/24-bxtwwksfpeezfjg.html

https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1637/

posted @ 2022-04-14 14:13  along_bro  阅读(1320)  评论(0编辑  收藏  举报