欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

给 EF Core 查询增加 With NoLock

程序员文章站 2022-03-12 18:48:34
EF Core 在 3.x 版本中增加了 `Interceptor`,使得我们可以在发生低级别数据库操作时作为 EF Core 正常运行的一部分自动调用它们。 例如,打开连接、提交事务或执行命令时。所以我们可以自定义一个 `Interceptor` 来记录执行的 sql 语句,也可以通过 `In... ......

给 ef core 查询增加 with nolock

intro

ef core 在 3.x 版本中增加了 interceptor,使得我们可以在发生低级别数据库操作时作为 ef core 正常运行的一部分自动调用它们。 例如,打开连接、提交事务或执行命令时。

所以我们可以自定义一个 interceptor 来记录执行的 sql 语句,也可以通过 interceptor 来实现 sql 语句的执行前的修改或者更准确的记录 dbcommand 执行的耗时。

这里我们可以借助 interceptor 实现对于查询语句的修改,自动给查询语句加 (with nolock)with nolock 等效于 read uncommited(读未提交)的事务级别,这样可能会造成一定的脏读,但是从效率上而言,是比较高效的,不会因为别的事务长时间未提交导致查询阻塞,所以对于大数据多事务的场景下,查询 sql 加 nolock 还是比较有意义的

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,配置如下:

var services = new servicecollection();
services.adddbcontext<testdbcontext>(options =>
{
    options
        .useloggerfactory(loggerfactory)
        .usesqlserver(dbconnectionstring)
        .addinterceptors(new querywithnolockdbcommandinterceptor())
        ;
});

使用效果

通过 loggerfactory 记录的日志查看查询执行的 sql 语句

给 EF Core 查询增加 With NoLock

可以看到查询语句自动加上了 with (nolock)

reference