当然不要忘记,官网才是最好的老师:docs.microsoft.com/zh-cn/dotnet/core/
沙盒学习指南: 免费环境docs.microsoft.com/zh-cn/learn/browse
posted @ 2020 初久的私房菜 推荐出品

EF 中的单查询与分查询

 

正文

介绍

在使用 Entity Framework (EF) 开发应用程序时,开发者常常需要在单查询和分查询之间做出选择。这个选择可能会对应用程序的性能产生显著影响。本文将探讨单查询和分查询的区别、各自带来的性能问题,以及如何在 EF 中实现分查询。

单查询的问题:笛卡尔爆炸

假设你有以下三个实体:DepartmentEmployeeProject。每个部门可以有多个员工和项目。以下是这些实体的简单表示:

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

public class Project
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int DepartmentId { get; set; }
}

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<Employee> Employees { get; set; } = new List<Employee>();
    public IList<Project> Projects { get; set; } = new List<Project>();
}

现在,我们希望查询所有部门,包括其员工和项目:

    .Include(d => d.Employees)
    .Include(d => d.Projects)
    .ToListAsync();

EF 会生成一个包含两个 LEFT JOIN 的 SQL 查询。由于 EmployeesProjectsDepartment 的同级关联集合,关系数据库会产生一个交叉乘积。这意味着 Employees 中的每一行都会与 Projects 中的每一行进行连接。

假设一个部门有 10 个员工和 10 个项目,数据库会返回 100 行数据。这种现象被称为 笛卡尔爆炸,即由于表之间的意外交叉连接(cross joins)导致查询结果数量异常增加。

所有这些不必要的数据都会被传输到客户端。如果数据库中有大量数据,或者我们包含更多同级的关联数据,性能问题可能会非常严重。

注意:当两个 JOIN 不在同一级别(即使用 ThenInclude 方法后跟在 Include 方法之后)时,不会发生笛卡尔爆炸。

分查询的解决方案

分查询可以解决笛卡尔爆炸问题。EF 会生成多个独立的查询,从而避免这个问题。

    .Include(d => d.Employees)
    .Include(d => d.Projects)
    .AsSplitQuery()
    .ToListAsync();

EF 会生成三个独立的查询。第一个查询选择 Departments,另外两个查询分别通过 INNER JOIN 包含 ProjectsEmployees

没有笛卡尔爆炸。

然而,这是三个独立的查询,需要三次往返数据库。这可能导致并发更新时结果不一致。可以使用可序列化(Serializable)或快照(Snapshot)事务隔离级别来缓解数据一致性问题。然而,这可能会带来其他性能和行为上的差异。

全局分查询配置

你可以将分查询配置为数据库上下文的默认行为。

    => optionsBuilder
        .UseSqlServer("[ConnectionString]",
            o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));

有了这样的配置,你仍然可以执行特定的单查询。

    .Include(d => d.Employees)
    .Include(d => d.Projects)
    .AsSingleQuery()
    .ToListAsync();

数据重复问题

回到我们最初的查询,只做一个 Include,看看 EF 生成的 SQL。

    .Include(d => d.Employees)
    .ToQueryString();

Department 的列(如 Name 列)会为每个 Employee 行重复。这通常是正常的,不会引起问题。

然而,如果我们的 Department 表有一个大列(例如,二进制数据、大文本等),那么这个大列将为每个 Employee 行重复。这也会导致类似笛卡尔爆炸的性能问题。在这种情况下,分查询也是一个不错的选择。

总结

在使用 Entity Framework 时,单查询和分查询各有优缺点。单查询可能会导致笛卡尔爆炸,而分查询虽然可以避免这一问题,但会增加数据库的往返次数,可能会影响数据一致性。根据你的具体需求和场景,选择合适的查询方式至关重要。通过合理使用分查询,你可以显著提升应用程序的性能和响应速度。

posted @   初久的私房菜  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
作者:初久的私房菜
好好学习,天天向上
返回顶部小火箭
好友榜:
如果愿意,把你的博客地址放这里
张弛:https://blog.zhangchi.fun/
点击右上角即可分享
微信分享提示