1. 程式人生 > 其它 >EF死鎖處理之:給 EF Core 查詢增加 With NoLock

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/