前言

最近将RabbitMQ正式封装引入到.NET Core 2.0项目当中,之前从未接触过这个高大上的东东跟着老大学习中,其中收获不少,本打算再看看RabbitMQ有时间写写,回来后和何镇汐大哥探讨了一点关于EF和EF Core的内容,于是乎本文就出来了。EF 6.x和EF Core中的查询缓存想必大家都有耳闻或者了解,从数据库中查询出来的实体会形成快照在内存中Copy一份且被上下文跟踪,接下来我们要讲的内容就是这个,我们来看看。

EF 6.x和EF Core查询缓存思考

首先我利用EF Core通过一个例子来进入今天的主题,在此过程中您也可多一点思考空间并对照和您想象中的结果是否有出入或者不一致的地方。

            var context = new EFCoreDbContext();
var blog1 = context.Blogs.Find();
Console.WriteLine($"数据库原始值为{blog1.Name}");
blog1.Name = "Jeffcky";
var blog2 = context.Blogs.FirstOrDefault(d => d.Id == );
Console.WriteLine($"查询数据库后的值为{blog2.Name}"

上述我们通过Find方法查询出Blog1,然后对Name进行赋值,紧接着我们再次查询主键等于3的实体,您猜想一下blog2中的Name会等于多少呢?

我们看看如上图所示结果为Jeffcky,有的童鞋就问了,我们进行第一次查询时值为3,然后我们仅仅只是赋值为Jeffcky并未提交,当我们再次查询相同实体时结果却为Jeffcky呢?难道不应该是3么,对吧。稍微对EF或者EF Core有所了解的大佬们明白第一次查询时会有快照,当下次查询时EF或者EF Core会在内存中根据第一次查询所对应的哈希值和主键去查找,此时查找到则直接利用内存中的对象,所以此时blog2的Name为Jeffcky。问题是不是到此就结束了呢?如果是这样,那我大半夜还浪费这时间写这篇博客!接下来我们再来看看两次查询所生成的SQL如何?

在EF 6.x和EF Core中 通过Find方法基于主键查询查询可重用,什么意思呢?如上第一个查询采用参数化查询,也就说如果我们下次再利用Find方法查询那么将不会到数据库查询,而是直接从内存中返回,有的童鞋就想了,恩,挺好,这样显著提高了查询性能,我只能说不一定看对应场景,如果对于非常频繁的查询我个人觉得不建议用此方法,因为还有对实体的更新操作啊,此时数据更新了却在内存中的值没有更新显示到UI上也就是说是过期了值,那么您觉得这个时候用Find方法还可取吗。对于第二个查询则直接采取赋值的形式(在我即将出版的《你必须掌握的EntityFramework 6.x和Core 2.0》书中有讲解EF 6.x查询的很大问题)这都不是事,问题是第二次我们利用FirstOrDefault方法查询此时居然走数据库了,不信,您可以看看如下利用SQL Profiler监控得到的SQL语句。

这就让人有点费解了,第二个查询既然是到数据库中查询那为何我们得到的值当前第一次查询出来修改但未被提交的值呢?这不是自相矛盾么,EF Core这样设计的意义何在,我也想不通,我能想到的是在同一上下文中大部分情况下不会对同一实体查询多次,但是谁能保证呢。 在EF Core中除了Find会进行翻译缓存,其他比如First、FirstOrDefault、Last、LastOrDefault都会到数据库中查询,当我们利用Last或者LastOrDefault查询时会出现更有意思的事情,我们看看生成的SQL语句:

var blog3 = context.Blogs.Last();

EF Core大哥哥们这么简单的查询问题都没测试到么,你翻译给我让我误以为这是返回所有列表了,结果一看查询出来的值却是对的,我想这是EF Core大哥哥们忘记加上TOP 1和ORDER BY了,到github上一搜索原来有大神提过这个ISSUE(https://github.com/aspnet/EntityFrameworkCore/issues/10493)在2.1版本发布会解决这个问题。好了我们回到主题所遇到的问题,在同一上下文查询同一实体,第一次查询如果我们利用Find方法查询会进行翻译缓存,待下一次再次查询时不会到数据库中查询,如果是下一次查询不是利用Find方法查询,比如FirstOrDefault此时会到数据库中查询但是此时的值却不是数据库中的值而是当前被修改而未被提交的值,那么我们如何获取数据库中的值而不是当前修改而未被提交的值呢?请往下看。

EF和EF Core获取数据库值而不是当前修改未被提交的值

我们讨论了问题的出现,接下来我们尝试利用方法来解决,我们可以对上下文所跟踪的实体进行移除通过Local.Remove,如下:

           var context = new EFCoreDbContext();

            var blog1 = context.Blogs.Find();
Console.WriteLine($"数据库原始值为{blog1.Name}");
blog1.Name = "Jeffcky";
context.Blogs.Local.Remove(blog1);
var blog2 = context.Blogs.FirstOrDefault(d => d.Id == );
Console.WriteLine($"查询数据库后的值为{blog2.Name}");

Local方法意味获取在本地所添加、修改等的实体,我们获取被上下文所本地被上下文所跟踪的实体然后移除,继而再进行查询是不是就可以移除呢?不好意思移除不了。

利用AsNoTracking方法

这个算是最简单的方法之一了,仅仅对于查询而言通过此方法不会形成快照从而提高查询性能。

            var context = new EFCoreDbContext();

            var blog1 = context.Blogs.Find();
Console.WriteLine($"数据库原始值为{blog1.Name}");
blog1.Name = "Jeffcky";
var blog2 = context.Blogs.AsNoTracking().FirstOrDefault(d => d.Id == );
Console.WriteLine($"查询数据库后的值为{blog2.Name}");

将实体状态标记为Detached不被上下文跟踪

           var context = new EFCoreDbContext();

            var blog1 = context.Blogs.Find();
Console.WriteLine($"数据库原始值为{blog1.Name}");
blog1.Name = "Jeffcky";
context.Entry(blog1).State = EntityState.Detached;
var blog2 = context.Blogs.FirstOrDefault(d => d.Id == );
Console.WriteLine($"查询数据库后的值为{blog2.Name}");

通过Reload方法刷新实体

            var context = new EFCoreDbContext();

            var blog1 = context.Blogs.Find();
Console.WriteLine($"数据库原始值为{blog1.Name}");
blog1.Name = "Jeffcky";
context.Entry(blog1).Reload();
var blog2 = context.Blogs.FirstOrDefault(d => d.Id == );
Console.WriteLine($"查询数据库后的值为{blog2.Name}");

通过GetDatabaseValues方法直接获取数据库中值

            var context = new EFCoreDbContext();

            var blog1 = context.Blogs.Find();
Console.WriteLine($"数据库原始值为{blog1.Name}");
blog1.Name = "Jeffcky";
var blog2 = (Blog)context.Entry(blog1).GetDatabaseValues().ToObject();
Console.WriteLine(blog2.Name);
//或者
var db = context.Entry(blog1).GetDatabaseValues();
Console.WriteLine(db["Name"]);

总结

好了今天的内容就到此为止了,无论是EF 6.x还是EF Core,只要我们对一些原理足够了解才不至于出现让人意想不到的问题。希望本文对您有所帮助,下节开始讲讲RabbitMQ,我们下节再会。

讨论过后而引发对EF 6.x和EF Core查询缓存的思考的更多相关文章

  1. Cookies 初识 Dotnetspider EF 6.x、EF Core实现dynamic动态查询和EF Core注入多个上下文实例池你知道有什么问题? EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    Cookies   1.创建HttpCookies Cookie=new HttpCookies("CookieName");2.添加内容Cookie.Values.Add(&qu ...

  2. C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现之方法二:加入缓存机制

    在上一篇文章中我用递归方法实现了管理菜单,在上一节我也提到要考虑用缓存,也算是学习一下.Net Core的缓存机制. 关于.Net Core的缓存,官方有三种实现: 1.In Memory Cachi ...

  3. EF中使用linq进行关联查询

    EF使用linq进行多表查询是完全可以的,最后ToList()调用的时候回产生一条分页的sql语句,所以并不是全部查询再分页的.所以不会影响查询的性能 public void TestLinq() { ...

  4. EF封装类 增加版,增加从缓存中查找数据方法,供参考!

    EF封装类 增加版,增加从缓存中查找数据方法,供参考! 这个类是抽象类,我这里增加了需要子类验证的方法ValidateEntity,方便扩展,若想直接使用该类,可以将该类更改成静态类,里面所有的方法都 ...

  5. EF中使用语句 或存储过程 查询(转)

    EF中使用语句 或存储过程 查询 1.无参数查询 var model = db.Database.SqlQuery("select* from UserInfoes ").ToLi ...

  6. EF 6.x、EF Core实现dynamic动态查询和EF Core实现多个上下文实例池你了解多少?

    前言 很长一段时间没有写博客了,今天补上一篇吧,偶尔发现不太愿意写博客了,太耗费时间,不过还是在坚持当中,毕竟或许写出来的东西能帮到一些童鞋吧,接下来我们直奔主题.无论是在在EF 6.x还是EF Co ...

  7. ASP.NET MVC EF直接更新数据(不需查询)

    EF(EntityFrameWork) ORM(对象关系映射框架/数据持久化框架),根据实体对象操作数据表中数据的一种面向对象的操作框架,底层也是调用ADO.NET ASP.NET MVC 项目会自动 ...

  8. ADO.NET EF 4.2 中的查询缓存(避免查询缓存)

    在WinForm系统中遇到了个问题,Form1是查询窗口,根据条件查询出所有数据,双击列表后创建弹出Form2窗口编辑单个记录,但编辑后保存后,在Form2中查询到的还是旧的数据,实际数据库中已经更新 ...

  9. ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 EF 框架服务 上一章节中我们了解了 Entity ...

随机推荐

  1. MIT许可证

    MIT许可证(The MIT License)是许多软件授权条款中,被广泛使用的其中一种.与其他常见的软件授权条款(如GPL.LGPL.BSD)相比,MIT是相对宽松的软件授权条款. MIT 许可证几 ...

  2. php过滤表单提交的html等危险代码

    表单提交如果安全做得不好就很容易因为这个表单提交导致网站被攻击了,下面我来分享两个常用的php过滤表单提交的危险代码的实例,各位有需要的朋友可参考. PHP过滤提交表单的html代码里可能有被利用引入 ...

  3. JavaScript实现轮播图(隔3秒显示一张图)

    <!DOCTYPE html><html> <head> <script> var time; function init(){ //获取div里面的东 ...

  4. 00-深入理解C#读书笔记说明

    带着问题去看书 尝试着,根据每一小节,先列出大纲.然后根据自己原先的认知和理解以及不理解,对每一个小的chapter,我会先自我提问,带着问题去阅读,然后把我的理解以及不理解记录下来,对于错误的地方做 ...

  5. Django REST framework+Vue 打造生鲜超市(八)

    九.个人中心功能开发 9.1.drf的api文档自动生成和 (1) url #drf文档,title自定义 path('docs',include_docs_urls(title='仙剑奇侠传')), ...

  6. C#之设计模式之六大原则(转载)

    设计模式之六大原则(转载) 关于设计模式的六大设计原则的资料网上很多,但是很多地方解释地都太过于笼统化,我也找了很多资料来看,发现CSDN上有几篇关于设计模式的六大原则讲述的比较通俗易懂,因此转载过来 ...

  7. JavaScript树(一) 简介

    树的相关术语 一个树结构包含一系列存在父子关系的节点. 每个节点都有一个父节点 (除了顶部的第一个节点)以及零个或多个子节点: 位于树顶部的节点叫作根节点(11) .它没有父节点.树中的每个元素都叫作 ...

  8. [LeetCode] Largest Plus Sign 最大的加型符号

    In a 2D grid from (0, 0) to (N-1, N-1), every cell contains a 1, except those cells in the given lis ...

  9. 出错信息:Incorrect string value: '\xE4\xBD\xA0\xE5\xA5\xBD' for column 'username'

    出错信息: java.sql.SQLException: Incorrect string value: '\xE4\xBD\xA0\xE5\xA5\xBD' for column 'username ...

  10. [Codeforces 922E]Birds

    Description 题库链接 一条直线上有 \(n\) 棵树,每棵树上有 \(c_i\) 只鸟,在一棵树底下召唤一只鸟的魔法代价是 \(cost_i\) 每召唤一只鸟,魔法上限会增加 \(B\) ...