前言

接下来会陆续详细讲解EF Core 2.1新特性,本节我们来讲讲EF Core 2.1新特性延迟加载,如果您用过EF 6.x就知道滥用延迟加载所带来的灾难,同时呢,对此深知的童鞋到了EF Core中也就造成了极大的心里阴影面积,那么到底该不该用呢?当然,完全取决于您。

如果初学者从未接触过EF 6.x,我们知道EF 6.x默认启用了延迟加载,所以这似乎有点强人所难的意味,在EF Core 2.1对于是否启用延迟加载通过单独提供包的形式来供我们所需,二者相对而言,EF 6.x对于延迟加载的使用是不明确、含糊其辞的,而EF Core 2.1对于延迟加载是很具体、明确的,如此一来至少不会造成滥用的情况。

深入理解EF Core 2.1延迟加载

在我个人公众号发过一篇文章也通过示例讲解了EF Core延迟加载的雏形早就有了,这里再稍微给个示例解释EF Core 2.1之前延迟加载的影子在哪里呢?我们依然给出已经用烂了的两个Blog和Post这两个类,如下:

  1. public class Blog
  2. {
  3. public int Id { get; set; }
  4. public string Name { get; set; }
  5. public string Url { get; set; }
  6. public DateTime CreatedTime { get; set; }
  7. public DateTime ModifiedTime { get; set; }
  8. public byte Status { get; set; }
  9. public bool IsDeleted { get; set; }
  10. public ICollection<Post> Posts { get; set; } = new List<Post>();
  11. }
  1. public class Post
  2. {
  3. public int Id { get; set; }
  4. public string Name { get; set; }
  5. public DateTime CreatedTime { get; set; }
  6. public DateTime ModifiedTime { get; set; }
  7.  
  8. public int? BlogId { get; set; }
  9. public Blog Blog { get; set; }
  10.  
  11. }

接下来我们进行如下查询,我们能够看到此时并未利用Include显式加载Posts,通过如下查询EF Core内部会进行关系修正从而查询出Posts。

  1. var dbContext = new EFCoreDbContext();
  2. var blog = dbContext.Blogs.FirstOrDefault();
  3. var posts = dbContext.Posts.Where(d => d.Blog.Id == blog.Id).ToList();

在EF Core 2.1中我们需要下载【Microsoft.EntityFrameworkCore.Proxies】包,同时在OnConfiguring方法中配置启用延迟加载代理,如下:

除了通过如上配置外导航属性必须用virtual关键字修饰(如上示例类未添加,请自行添加),否则将抛出异常,你懂的。接下来在完全配置好延迟加载的前提下再来进行如下查询,此时在我们需要用到Posts时才会去数据库中查询。

  1. var dbContext = new EFCoreDbContext();
  2. var blog = dbContext.Blogs.FirstOrDefault();
  3. var posts = blog.Posts.ToList();

对于EF Core中的延迟加载和EF 6.x使用方式无异,接下来我们再来看看官网给出了未启用代理也可进行延迟加载,通过安装【Microsoft.EntityFrameworkCore.Abstractions 】包引用ILazyLoader服务进行实现,如下:

  1. public class Blog
  2. {
  3. private ILazyLoader LazyLoader { get; set; }
  4.  
  5. public Blog(ILazyLoader lazyLoader)
  6. {
  7. LazyLoader = lazyLoader;
  8. }
  9. public int Id { get; set; }
  10. public string Name { get; set; }
  11. public string Url { get; set; }
  12. public DateTime CreatedTime { get; set; }
  13. public DateTime ModifiedTime { get; set; }
  14. public byte Status { get; set; }
  15. public bool IsDeleted { get; set; }
  16.  
  17. private ICollection<Post> _posts;
  18.  
  19. public ICollection<Post> Posts
  20. {
  21. get => LazyLoader?.Load(this, ref _posts);
  22. set => _posts = value;
  23. }
  24. }
  1. public class Post
  2. {
  3. private ILazyLoader LazyLoader { get; set; }
  4. public Post(ILazyLoader lazyLoader)
  5. {
  6. LazyLoader = lazyLoader;
  7. }
  8. public int Id { get; set; }
  9. public string Name { get; set; }
  10. public DateTime CreatedTime { get; set; }
  11. public DateTime ModifiedTime { get; set; }
  12.  
  13. public int? BlogId { get; set; }
  14.  
  15. private Blog _blog;
  16. public Blog Blog
  17. {
  18. get => LazyLoader?.Load(this, ref _blog);
  19. set => _blog = value;
  20. }
  21. }

通过如上注入ILazyLoader服务依附于实体上最终实现延迟加载,这么做对于启用延迟加载代理的最大好处在于只针对特定实体进行延迟加载,而使用延迟加载代理则是全局配置,所有实体都必须通过virtual关键字修饰且都会实现延迟加载。同时呢,通过如下图可知,此时ILazyLoader依附在上下文中也就是说一旦创建实体实例将实现延迟加载。

当然无论是EF 6.x还是EF Core 2.1进行如下查询,那么结果对于如下第二次查询的导航属性将不会再去数据库中查询,因为主体对应的导航属性在内存中已存在,此时将直接返回,也就是说主体查询两次,而依赖实体则将只执行一次查询。

  1. var dbContext = new EFCoreDbContext();
  2. var blog = dbContext.Blogs.FirstOrDefault();
  3. var posts = blog.Posts.ToList();
  4.  
  5. var blog1 = dbContext.Blogs.FirstOrDefault();
  6. var posts1 = blog1.Posts.ToList();

接下来我们再来看看在上下文实例池中启用延迟加载并结合显式加载看看EF Core执行策略是怎样的呢?

  1. var services = new ServiceCollection();
  2. services.AddDbContextPool<EFCoreDbContext>(options =>
  3. {
  4. var loggerFactory = new LoggerFactory();
  5. loggerFactory.AddConsole(LogLevel.Debug);
  6. options.UseLazyLoadingProxies().UseLoggerFactory(loggerFactory).UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=EFCore2xDb;integrated security=True;MultipleActiveResultSets=True;");
  7. });
  8.  
  9. var serviceProvider = services.BuildServiceProvider();
  10.  
  11. var dbContext = serviceProvider.GetRequiredService<EFCoreDbContext>();
  12.  
  13. var blog = dbContext.Blogs.Include(d => d.Posts).Last();
  14. var posts = blog.Posts.FirstOrDefault();
  15.  
  16. var blog1 = dbContext.Blogs.FirstOrDefault();
  17. var posts1 = blog1.Posts.FirstOrDefault();

上述查询并结合最终生成的SQL得知:当没有执行显式加载时若我们启用延迟加载则强制执行延迟加载,否则执行显式加载,同时我们通过验证也得知注入ILazyLoader服务后并调用Load方法加载关联实体内部本质则是若要访问的关联实体在内存中不存在时则执行RPC加载关联实体,否则将从内存中获取。

如上所述若我们没有显式进行饥饿加载,那么在启用延迟加载的前提下对于任何关联实体是否都会执行延迟加载呢?比如复杂属性呢?我们再来看看如下示例(请自行在上述配置上下文实例池中启用延迟加载代理)。

  1. public class StreetAddress
  2. {
  3. public string Street { get; set; }
  4. public string City { get; set; }
  5. }
  6.  
  7. public class Order
  8. {
  9. public int Id { get; set; }
  10. public virtual StreetAddress ShippingAddress { get; set; }
  11. }
  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.Entity<Order>(e =>
  4. {
  5. e.ToTable("Orders");
  6.  
  7. e.HasKey(k => k.Id);
  8.  
  9. e.OwnsOne(o => o.ShippingAddress);
  10. });
  11. }
  1. var order = dbContext.Orders.FirstOrDefault();
  2. var shippingAddress = order.ShippingAddress;

我们从如上图生成的SQL得知:对于复杂属性即通过OwnOne配置的复杂属性在查询时,EF Core会一次性将复杂属性值全部返回(也就是说复杂属性值总是会加载),所以对于启用延迟加载不会起到任何作用,即使是通过Include进行显式加载都是多此一举。

延迟加载避免循环引用

无论是EF 6.x还是EF Core 2.1中的延迟加载都无法避免循环引用问题,若在.NET Core Web应用程序中启用了延迟加载,我们需要在ConfigureServices方法中进行如下配置才行。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddMvc().AddJsonOptions(
  4. options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
  5. }

在写博客期间有一些童鞋在评论中留言问我是如何学习的,每个人学习方式肯定不一样,但是绝对少不了多敲代码,这里就有一个实际例子(这里不是批评那位童鞋哈),有一位童鞋购买了我写的书(首先感谢那位童鞋),看到某一章节问我那里是不是错了,我一看大概就问了那位童鞋是不是没敲代码,个人觉得无论是看技术书还是看技术博客,还是要敲一遍为好,因为那个章节的某一个类是在某一命名空间下的,估计那位童鞋没见过所以以为错了,别光顾着看,多敲敲代码验证验证总没错的。

评论送书规则

6月、19、20、21、22总计4天,在本帖,每天上午10点的第一个回帖评论者,分别赠送本书1本,good luck to u(如果您需要签名留作纪念的话私信我可告知,默认不需要,虽然我字写的很丑)。

同一ID不可以重复参与活动,重复的话,取紧接着的下一个人。不允许用程序刷屏,一旦发现,取消资格。

明确确认您满足以上规则后,请写下您的地址、姓名、邮编、手机号私信给我,以便后续邮寄。

深入了解EntityFramework Core 2.1延迟加载(Lazy Loading)的更多相关文章

  1. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  2. Entity Framework加载相关实体——延迟加载Lazy Loading、贪婪加载Eager Loading、显示加载Explicit Loading

    Entity Framework提供了三种加载相关实体的方法:Lazy Loading,Eager Loading和Explicit Loading.首先我们先来看一下MSDN对三种加载实体方法的定义 ...

  3. Lazy Loading | Explicit Loading | Eager Loading in EntityFramework and EntityFramework.Core

    EntityFramework Eagerly Loading Eager loading is the process whereby a query for one type of entity ...

  4. EntityFramework 7 更名为EntityFramework Core(预发布状态)

    前言 最近很少去学习和探索新的东西,尤其是之前一直比较关注的EF领域,本身不太懒,但是苦于环境比较影响自身的心情,所以迟迟没有下笔,但是不去学习感觉在精神层面缺少点什么,同时也有园友说EF又更新了,要 ...

  5. EntityFramework Core笔记:查询数据(3)

    1. 基本查询 1.1 加载全部数据 using System.Linq; using (var context = new LibingContext()) { var roles = contex ...

  6. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  7. EntityFramework Core技术线路(EF7已经更名为EF Core,并于2016年6月底发布)

    官方文档英文地址:https://github.com/aspnet/EntityFramework/wiki/Roadmap 历经延期和更名,新版本的实体框架终于要和大家见面了,虽然还有点害羞.请大 ...

  8. EntityFramework Core 1.1有哪些新特性呢?我们需要知道

    前言 在项目中用到EntityFramework Core都是现学现用,及时发现问题及时测试,私下利用休闲时间也会去学习其他未曾遇到过或者用过的特性,本节我们来讲讲在EntityFramework C ...

  9. EntityFramework 6.x和EntityFramework Core必须需要MultipleActiveResultSets?

    前言 本节我们来探讨到底需不需要在连接字符串上加上MultipleActiveResultSets = true ?,若您有更深层次的理解欢迎留下您的脚印. EntityFramework 6.x和E ...

随机推荐

  1. 再谈AbstractQueuedSynchronizer1:独占模式

    关于AbstractQueuedSynchronizer JDK1.5之后引入了并发包java.util.concurrent,大大提高了Java程序的并发性能.关于java.util.concurr ...

  2. 史上最全 40 道 Dubbo 面试题及答案,看完碾压面试官!

    想往高处走,怎么能不懂 Dubbo? Dubbo是国内最出名的分布式服务框架,也是 Java 程序员必备的必会的框架之一.Dubbo 更是中高级面试过程中经常会问的技术,无论你是否用过,你都必须熟悉. ...

  3. Python基础(os模块)

    os模块用于操作系统级别的操作: os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目录:相当 ...

  4. mysql的学习笔记(八)

    1.存储引擎(表类型) mysql将数据以不同的技术存储在文件(内存)中,这种技术称为存储引擎.每一种存储引擎使用不同的存储机制,索引技巧,锁定水平,提供广泛且不同的功能. mysql支持的存储引擎 ...

  5. GlideNewDemo【Glide4.7.1版本的简单使用以及圆角功能】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 简单记录下Glide4.7.1版本的使用和实现圆角方案. 注意:关于详细使用请仔细阅读<官方指南>. 效果图 使用步骤 ...

  6. ViewPagerWithRecyclerDemo【RecyclerView+ViewPager实现类似TabLayout+ViewPager效果】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 使用RecyclerView+ViewPager实现类似TabLayout+ViewPager效果. 效果图 使用步骤 一.项目组织 ...

  7. (二)surging 微服务框架使用系列之surging 的准备工作consul安装

    suging 的注册中心支持consul跟zookeeper.因为consul跟zookeeper的配置都差不多,所以只是consul的配置 consul下载地址:https://www.consul ...

  8. RESTful 规范

    RESTful 规范 前言 rest 是一种软件架构风格,如果使用的是 rest 接口,那么就可以说你的接口是 restful. rest接口是围绕''资源''展开的,利用 HTTP 的协议,其实 r ...

  9. 整合 MyPerf4J 做Java性能监控和统计工具

    快速启动MyPerf4J MyPerf4J 采用 JavaAgent 配置方式,透明化接入应用,对应用代码完全没有侵入. 打包 项目地址: https://github.com/LinShunKang ...

  10. ado.net的简单数据库操作(三)——简单增删改查的实际应用

    果然,在犯困的时候就该写写博客,写博客就不困了,哈哈! 上篇我记录了自己的SqlHelper的开发过程,今天记录一下如何使用这个sqlhelper书写一个具有简单增删改查的小实例啦. 实例描述:在数据 ...