给 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
推荐阅读
