用户登录
用户注册

分享至

给 EF Core 查询增加 With NoLock

  • 作者: 鉃落的幽靈
  • 来源: 51数据库
  • 2021-09-03

给 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 语句

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

reference

  • https://github.com/weihanli/weihanli.entityframework/blob/dev/src/weihanli.entityframework/interceptors/querywithnolockdbcommandinterceptor.cs
软件
前端设计
程序设计
Java相关