【Entity Framework】你必须要了解EF中数据查询之数据加载

news2024/11/27 0:25:59

【Entity Framework】你必须要了解EF中数据查询之数据加载

文章目录

  • 【Entity Framework】你必须要了解EF中数据查询之数据加载
    • 一、概述
    • 二、预先加载
      • 2.1 包含多个层级
      • 2.2 经过筛选的包含
    • 三、显示加载
      • 3.1查询关联实体
    • 四、延时加载
      • 4.1 不使用代理进行延迟加载

在这里插入图片描述

一、概述

Entity Framework Core允许在模型中使用导航属性来加载关联实体。有三种常见的O/RM模式可用于加载关联数据。

  • 预先加载表示从数据库中加载关联数据,作为初始查询的一部分;
  • 显示加载表示稍后从数据库中显示加载关联数据;
  • 延迟加载表示在访问导航属性时,从数据库中以透明方式加载关联数据;

二、预先加载

可以使用Include方法来指定要包含在查询结果中的关联数据。如示例,结果中,结果中返回的blogs将使用关联的posts填充其posts属性。

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ToList();
}

Entity Framework Core 会根据之前已加载到上下文实例中的实体自动填充导航属性。 因此,即使不显式包含导航属性的数据,如果先前加载了部分或所有关联实体,则仍可能填充该属性。

可以在单个查询中包含多个关系的关联数据。

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .Include(blog => blog.Owner)
        .ToList();
}

2.1 包含多个层级

使用ThenInclude方法可以依循关系包含多个层级的关联数据。以下示例加载了所有博客,其相关文章及每篇文章的作者。

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ToList();
}

可通过链式调用ThenInclude,进一步包含更深级别的关联数据。

using(var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog       => blog.Posts)
        .ThenInclude(post   => post.Author)
        .ThenInclude(author => author.Photo)
        .ToList();
}

可以将对来自多个级别和多个根的关联数据的所有调用合并到同一查询中。

using(var context=new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog       => blog.Posts)
        .ThenInclude(post   => post.Author)
        .ThenInclude(author => post.Photo)
        .Include(blog       => blog.Owner)
        .ThenInclude(owner  => owner.Photo)
        .ToList();
}

你可能希望将已包含的某个实体的多个关联实体都包含进来。 例如,当查询 Blogs 时,你会包含 Posts,然后希望同时包含 PostsAuthorTags。 为了包含这两项内容,需要从根级别开始指定每个包含路径。 例如,Blog -> Posts -> AuthorBlog -> Posts -> Tags。 这并不意味着会获得冗余联接查询,在大多数情况下,EF 会在生成 SQL 时合并相应的联接查询。

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags)
        .ToList();
}

2.2 经过筛选的包含

在应用包含功能来加载相关数据时,可对已包含的集合导航应用某些可枚举的操作,这样就可以对结果进行筛选和排序。

支持的操作包括:WhereOrderByOrderByDescendingThenByThenByDescendingSkipTake

应对传递到Include方法的Lambda中的集合导航应用这类操作。如下例所示:

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToList();
}

可对多次包含的每个导航应用相同的操作:

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

三、显示加载

可以通过DbContext.Entry(...) API显示加载导航属性。

using(var context=new BloggingContext())
{
    var blog = Context.Blogs.Single(b=>b.BlogId == 1);
    context.Entry(blog).Collection(b=>b.Posts).Load();
    context.Entry(blog)
        .Reference(b => b.Owner)
        .Load();
}

还可以通过执行返回关联实体的单独查询来显式加载导航属性。 如果已启用更改跟踪,则在查询具体化实体时,EF Core 将自动设置新加载的实体的导航属性以引用任何已加载的实体,并设置已加载实体的导航属性以引用新加载的实体。

3.1查询关联实体

可以获得表示导航属性内容的LINQ查询。

这使你可对查询应用其他运算符。示例:

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);
    var postCount = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Count();
}

还可以筛选要加载到内存中的关联实体。

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);
    var goodPosts = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Where(p => p.Rating > 3)
        .ToList();
}

四、延时加载

使用延迟加载的最简单方法是通过安装Microsoft.EntityFrameworkCore.Proxies包,并通过调用UseLazyLoadingProxies来启动该包。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseLazyLoadingProxies()
        .UseSqlServer(myConnectionString);

或在使用 AddDbContext 时:

.AddDbContext<BloggingContext>(
    b => b.UseLazyLoadingProxies()
          .UseSqlServer(myConnectionString));

EF Core将为可被重写的任何导航属性启用延迟加载。

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public virtual Blog Blog { get; set; }
}

4.1 不使用代理进行延迟加载

不使用代理进行延迟加载的工作方式是将 ILazyLoader 注入到实体中,如实体类型构造函数中所述。例如:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts
    {
        get => LazyLoader.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader.Load(this, ref _blog);
        set => _blog = value;
    }
}

此方法不要求实体类型为可继承的类型,也不要求导航属性必须是虚拟的,且允许通过new创建的实体实例在附加到上下文后可进行延迟加载。但它需要对Microsoft.EntityFrameworkCore.Abstractions包中定义的ILazyLoader服务的引用。此包包含所允许的最少的一组类型,以便将依赖此包时所产生的影响将到最低。不过,可以将ILazyLoader.Load方法以委托的形式注入,这样就可以完全避免依赖于实体类型的任何EF Core包。

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts
    {
        get => LazyLoader.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader.Load(this, ref _blog);
        set => _blog = value;
    }
}

上述代码使用 Load 扩展方法,以便更干净地使用委托:

public static class PocoLoadingExtensions
{
    public static TRelated Load<TRelated>(
        this Action<object, string> loader,
        object entity,
        ref TRelated navigationField,
        [CallerMemberName] string navigationName = null)
        where TRelated : class
    {
        loader?.Invoke(entity, navigationName);

        return navigationField;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1599854.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言】带你完全理解指针(六)指针笔试题

目录 1. 2. 3. 4. 5. 6. 7. 8. 1. int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】 2&#xff0c;5 【解析】 定义了一个指向整数的指针ptr&#xff0c;并将其初始化为&…

设计编程网站集:动物,昆虫,蚂蚁养殖笔记

入门指南 区分白蚁与蚂蚁 日常生活中&#xff0c;人们常常会把白蚁与蚂蚁搞混淆&#xff0c;其实这两者是有很大区别的&#xff0c;养殖方式差别也很大。白蚁主要食用木质纤维&#xff0c;会给家庭房屋带来较大危害&#xff0c;而蚂蚁主要采食甜食和蛋白质类食物&#xff0c;不…

pytorch 今日小知识3——nn.MaxPool3d 、nn.AdaptiveAvgPool3d、nn.ModuleList

MaxPool3d — PyTorch 2.2 documentation 假设输入维度&#xff08;1,2,3,4,4&#xff09; maxpool torch.nn.MaxPool3d(kernel_size(2, 2, 2), stride(2, 2, 2), padding(1, 0, 0))F 维的 kernel_size 为 2&#xff0c;说明在 F 维的覆盖的 frame 数为 2&#xff0c;也就是…

Buck变换电路

Buck变换电路 Buck变换电路是最基本的DC/DC拓扑电路&#xff0c;属于非隔离型直流变换器&#xff0c;其输出电压小于输入电压。Buck变换电路具有效率高、输出稳定、控制简单和成本低的优点&#xff0c;广泛应用于稳压电源、光伏发电、LED驱动和能量回收系统。 电路原理 Buck变…

Noisy Student(CVPR 2020)论文解读

paper&#xff1a;Self-training with Noisy Student improves ImageNet classification official implementation&#xff1a;https://github.com/google-research/noisystudent 本文的创新点 本文提出了一种新的半监督方法Noisy Student Training&#xff0c;主要包括三步…

每日一题 — 串联所有单词的子串

30. 串联所有单词的子串 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;因为words里面的每一个字符串的长度都是固定的&#xff0c;所以可以将题转换成字符在字符串中的所有异位词 设出哈希表定义left和right进窗口维护count判断出窗口维护count 代码&#xff1a; …

Langchain入门到实战-第三弹

Langchain入门到实战 Langchain中RAG入门官网地址Langchain概述代码演示调用RAG功能更新计划 Langchain中RAG入门 Retrieval Augmented Generation 翻译成中文是“检索增强生成” 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息…

算法打卡day35

今日任务&#xff1a; 1&#xff09;343. 整数拆分 2&#xff09;96.不同的二叉搜索树 3&#xff09;复习day11 343. 整数拆分 题目链接&#xff1a;343. 整数拆分 - 力扣&#xff08;LeetCode&#xff09; 给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xf…

Vue3 + Element-Plus 使用 Table 插槽时数据未及时更新

Vue3 Element-Plus 使用 Table 插槽时数据未及时更新 问题重现解决方法最终效果 问题重现 这里我已经通过二级分类 id 查询到一级分类和二级分类&#xff0c;但是使用插槽和 v-for 渲染出来还是之前的分类 id&#xff0c;但是一点击表格或者保存代码他又能正常刷新出来。 <…

算法设计与分析实验报告c++实现(矩阵链相乘、投资问题、完全背包问题、数字三角形、最小生成树、背包问题)

一、实验目的 1&#xff0e;加深学生对分治法算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 用动态…

1.2MHz,固定频率白光LED驱动器

一、产品概述 TX6216是一款升压转换器&#xff0c;设计用于通过单节锂离子电池驱动多达7个串联的白光LED。 TX6216采用电流模式&#xff0c;固定频率架构来调节LED电流&#xff0c;LED电流通过外部电流检测电阻测量。其低104mV反馈电压可降低功率损耗并提高效率。 TX6216具有…

笔试题1 -- 吃掉字符串中相邻的相同字符(点击消除_牛客网)

吃掉字符串中相邻的相同字符 文章目录 吃掉字符串中相邻的相同字符题目重现解法一&#xff1a;(基于 erase() 函数实现)解法二&#xff1a;&#xff08;利用 栈 辅助实现&#xff09;总结 题目链接&#xff1a; 点击消除_牛客网 题目重现 牛牛拿到了一个字符串。 他每次“点击…

JMeter控制器数据库获取一组数据后遍历输出

目录 1、测试计划中添加Mysql Jar包 2、添加线程组 3、添加 jdbc connection configuration 4、添加JDBC Request&#xff0c;从数据库中获取数据 5.获取数据列表&#xff0c;提取所有goodsName信息 6.通过添加控制器遍历一组数据 6.1 方式一&#xff1a;循环控制器方式 …

Vue3从入门到实战:深度掌握通信插槽slot

slot_默认插槽的概念&#xff1a; 在Vue中&#xff0c;插槽&#xff08;slot&#xff09;是一种用于在组件中插入内容的特殊技术。默认插槽是其中一种类型的插槽&#xff0c;它允许你在组件的模板中指定一个位置&#xff0c;以便在使用组件时插入自定义的内容。 想象一下你有…

Linux中安装seata

Linux中安装seata 一、准备1、环境2、下载3、上传到服务器4、解压 二、配置1、备份配置文件2、导入sql3、修改配置前4、修改配置后5、在nacos中配置 三、使用1、启动2、关闭 一、准备 1、环境 因为要在 nacos 中配置&#xff0c;要求安装并启动 nacos 。可以参考这篇博客。 …

【十一】MyBatis Plus 原理分析

MyBatis Plus 原理分析 摘要 Java EE开发中必不可少ORM框架&#xff0c;目前行业里最流行的orm框架非Mybatis莫属了&#xff0c;而Mybatis框架本身没有提供api实现&#xff0c;所以市面上推出了Mybatis plus系列框架&#xff0c;plus版是mybatis增强工具&#xff0c;用于简化My…

【JAVA基础篇教学】第十二篇:Java中多线程编程

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第十二篇&#xff1a;Java中多线程编程。 多线程编程是利用多个线程同时执行任务来提高程序的效率和性能。在 Java 中&#xff0c;多线程编程可以通过继承 Thread 类或实现 Runnable 接口来实现。下面是一个简单的多线程…

IntelliJ IDEA2024 安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 IDEA&#xff08;Integrated Development Environment for Apache&#xff09; 是一款专为 Apache 开发者设计的集成开发环境。该软件提供了丰富的功能和工具&#xff0c;帮助开发者更高效地创建、调试和部署 Apache 项目。 主…

自定义类型: 结构体 (详解)

本文索引 一. 结构体类型的声明1. 结构体的声明和初始化2. 结构体的特殊声明3. 结构体的自引用 二. 结构体内存对齐1. 对齐规则2. 为啥存在对齐?3. 修改默认对齐值 三. 结构体传参四. 结构体实现位段1. 什么是位段?2. 位段的内存分配3. 位段的应用4. 位段的注意事项 ​ 前言:…

计算机系列之操作系统的系统

2、大话操作系统的启动 当按下开机键时&#xff0c;BIOS 就会开始执行 ​ BIOS 就是放在主板上 ROM 里面的一段程序。 ​ ROM Read Only Memory&#xff08;只能读取的内存&#xff09; ​ 所以 BIOS 在出厂的时候就可以直接写死在 ROM 里面。 ​ 每次开机的时候&#xff…