EntityFramework Core 3.x添加查询提示(NOLOCK)
前言
今天看到有园友写了一篇关于添加NOLOCK查询提示的博文《https://www.cnblogs.com/weihanli/p/12623934.html》,这里呢,我将介绍另外一种添加查询提示的方法,此方式源于我看过源码后的实现,孰好孰歹,请自行判之,接下来我们一起来看看。
查询提示(NOLOCK)
在EntityFramework中,如需要添加查询提示需要自定义实现拦截器,但在EntityFramework Core中除了支持实现自定义拦截器外,还可以通过继承自对应类进行复写,那就是QuerySqlGenerator类,存在于命名空间【Microsoft.EntityFrameworkCore.Query】,在此类通过我们所写的表达式实现所有查询组合,比如我们需要用到的对表的设置,如下:
protected override Expression VisitTable(TableExpression tableExpression) { _relationalCommandBuilder .Append(_sqlGenerationHelper.DelimitIdentifier(tableExpression.Name, tableExpression.Schema)) .Append(AliasSeparator) .Append(_sqlGenerationHelper.DelimitIdentifier(tableExpression.Alias)); return tableExpression; }
同时我们可以看到还有另外一个类SqlServerQuerySqlGenerator继承自上述类,若我们需要重写的话继承自此类即可,比如在此类中进一步重写了三个表达式,我们随便看一个,如下:
protected override void GenerateTop(SelectExpression selectExpression) { if (selectExpression.Limit != null && selectExpression.Offset == null) { Sql.Append("TOP("); Visit(selectExpression.Limit); Sql.Append(") "); } }
上述意在表明:当我们进行在内存中通过Skip和Take进行分页时,因为Skip会翻译成Offset,而Take会翻译成Limit,若我们直接跳过Skip而写Take,此时在生成的Sql语句中添加TOP,很显然这是合情合理而且合法的。举个栗子,如下:
var context = new EFCoreDbContext(); context.Database.EnsureCreated(); var blogs = context.Blogs.Take(3).ToList();
那么此类是何时进行实例化的呢?通过SqlServerQuerySqlGeneratorFactory工厂类实例化,如下:
public class SqlServerQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory { private readonly QuerySqlGeneratorDependencies _dependencies; public SqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies) { _dependencies = dependencies; } public virtual QuerySqlGenerator Create() => new SqlServerQuerySqlGenerator(_dependencies); }
那么上述Sql查询工厂类到底具体是在什么时候被注册的呢,如下已省略其他注册类:
public static IServiceCollection AddEntityFrameworkSqlServer([NotNull] this IServiceCollection serviceCollection) { Check.NotNull(serviceCollection, nameof(serviceCollection)); var builder = new EntityFrameworkRelationalServicesBuilder(serviceCollection) // New Query Pipeline .TryAdd<IQuerySqlGeneratorFactory, SqlServerQuerySqlGeneratorFactory>() builder.TryAddCoreServices(); return serviceCollection; }
通过上述AddEntityFrameworkSqlServer名称可猜测该方法肯定是在实例化上下文时注册所有需要用到的接口具体实现,有了这个就好办了,为了不破坏原有的实现,我们自定义Sql查询生成类并继承自SqlServerQuerySqlGenerator并重写对表的设置并添加NOLOCK查询提示,如下:
public class CustomSqlServerQuerySqlGenerator : SqlServerQuerySqlGenerator { public CustomSqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies) : base(dependencies) { } protected override Expression VisitTable(TableExpression tableExpression) { var result = base.VisitTable(tableExpression); Sql.Append(" WITH (NOLOCK)"); return result; } }
接下来我们则需要实现自定义查询工厂并继承自默认提供的查询工厂类从而实例化上述自定义的查询类,如下:
public class CustomSqlServerQuerySqlGeneratorFactory : SqlServerQuerySqlGeneratorFactory { private readonly QuerySqlGeneratorDependencies _dependencies; public CustomSqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies) : base(dependencies) { _dependencies = dependencies; } public override QuerySqlGenerator Create() => new CustomSqlServerQuerySqlGenerator(_dependencies); }
那我们如何将默认提供的查询工厂类替换为上述自定义查询工厂类呢?稍微对DbContextOptionsBuilder类有所了解的童鞋应该知道,在该类中提供了ReplaceService方法来给我们替换EF Core中默认的实现,如下:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseLoggerFactory(loggerFactory) .UseSqlServer(@"Server=.;Database=EFCore;Trusted_Connection=True;") .ReplaceService<IQuerySqlGeneratorFactory, CustomSqlServerQuerySqlGeneratorFactory>();
到此就已经实现了添加NOLOCK查询提示,对于此种实现方式同样应该也适用于2.x版本,只不过稍微注意下对于自定义类构造函数参数可能略有不同,对于自定义实现,还是写成扩展方法比较好,这样也方便统一管理,看个人诺,比如写成如下:
public static class CustomDbContextOptionsBuilderExtensions { public static DbContextOptionsBuilder UseCustomSqlServerQuerySqlGenerator(this DbContextOptionsBuilder optionsBuilder) { optionsBuilder.ReplaceService<IQuerySqlGeneratorFactory, CustomSqlServerQuerySqlGeneratorFactory>(); return optionsBuilder; } }
总结
通过拦截器或者本节从源头生成Sql语句时添加对表的查询提示皆可,到底哪一个好呢?自行判断吧,其他就没啥可以进行总结的了,暂时到此为止吧。

为了方便大家在移动端也能看到我分享的博文,现已注册个人公众号,扫描上方左边二维码即可,欢迎大家关注,有时间会及时分享相关技术博文。
感谢花时间阅读此篇文章,如果您觉得这篇文章你学到了东西也是为了犒劳下博主的码字不易不妨打赏一下吧,让楼主能喝上一杯咖啡,在此谢过了!
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!
本文版权归作者和博客园共有,来源网址:http://www.cnblogs.com/CreateMyself)/欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2017-04-03 EntityFramework Core并发深挖详解,一纸长文,你准备好看完了吗?