給 EF Core 查詢增長 With NoLock

給 EF Core 查詢增長 With NoLock

Intro

EF Core 在 3.x 版本中增長了 Interceptor,使得咱們能夠在發生低級別數據庫操做時做爲 EF Core 正常運行的一部分自動調用它們。 例如,打開鏈接、提交事務或執行命令時。git

因此咱們能夠自定義一個 Interceptor 來記錄執行的 sql 語句,也能夠經過 Interceptor 來實現 sql 語句的執行。github

這裏咱們能夠藉助 Interceptor 實現對於查詢語句的修改,自動給查詢語句加 (WITH NOLOCK)WITH NOLOCK 等效於 READ UNCOMMITED(讀未提交)的事務級別,這樣會形成必定的髒讀,可是從效率上而言,是比較高效的,不會由於別的事務長時間未提交致使查詢阻塞,因此對於大數據場景下,查詢 SQL 加 NOLOCK 仍是比較有意義的sql

NoLockInterceptor

繼承 DbCommandInterceptor,重寫查詢 sql 執行以前的操做,在執行 sql 以前增長 WITH(NOLOCK),實現代碼以下:數據庫

public class QueryWithNoLockDbCommandInterceptor : DbCommandInterceptor
{
    private static readonly Regex TableAliasRegex =
        new Regex(@"(?<tableAlias>AS \[[a-zA-Z]\w*\](?! WITH \(NOLOCK\)))",
            RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.IgnoreCase);

    public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
    {
        command.CommandText = TableAliasRegex.Replace(
            command.CommandText,
            "${tableAlias} WITH (NOLOCK)"
            );
        return base.ScalarExecuting(command, eventData, result);
    }

    public override Task<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result,
        CancellationToken cancellationToken = new CancellationToken())
    {
        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)
    {
        command.CommandText = TableAliasRegex.Replace(
            command.CommandText,
            "${tableAlias} WITH (NOLOCK)"
            );
        return result;
    }

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

Interceptor 的使用

在註冊 DbContext 服務的時候,能夠配置 Interceptor,配置以下:ide

var services = new ServiceCollection();
services.AddDbContext<TestDbContext>(options =>
{
    options
        .UseLoggerFactory(loggerFactory)
        .UseSqlServer(DbConnectionString)
        .AddInterceptors(new QueryWithNoLockDbCommandInterceptor())
        ;
});

使用效果

經過 loggerFactory 記錄的日誌查看查詢執行的 sql 語句大數據

能夠看到查詢語句自動加上了 WITH (NOLOCK)日誌

Reference

相關文章
相關標籤/搜索