C# Entity Framework 中的 SQL 请求取决于延迟加载或急切加载

Posted

技术标签:

【中文标题】C# Entity Framework 中的 SQL 请求取决于延迟加载或急切加载【英文标题】:SQL requests in C# Entity Framework depending on lazy or eager loading 【发布时间】:2020-03-10 18:51:14 【问题描述】:

我有一个简单的连接查询,连接两个表(一个类别有很多产品):

using (ProdContext db = new ProdContext())

    var query = from category in db.Categories
                join product in db.Products
                     on category.CategoryID equals product.CategoryID into productList
                select new
                       
                           categoryName = category.Name,
                           products = productList
                       ;

    foreach (var c in query)
    
        Console.WriteLine("* 0", c.categoryName);

        foreach (var p in c.products)
        
             Console.WriteLine("   - 0", p.Name);
        
    ;

对于课程:

class Category

    public int CategoryID  get; set; 
    public String Name  get; set; 
    public List<Product> Products  get; set; 


class Product

    public int ProductID  get; set; 
    public String Name  get; set; 
    public int UnitsInStock  get; set; 
    public int CategoryID  get; set; 


class ProdContext : DbContext

    public DbSet<Category> Categories  get; set; 
    public DbSet<Product> Products  get; set; 

通常一切都很好,但是当我开始尝试急切加载和延迟加载时,我感到很困惑,因为无论我是否在查询末尾添加.ToList(),我的 SQL 请求总是如下所示:

SELECT 
    [Project1].[CategoryID] AS [CategoryID], 
    [Project1].[Name] AS [Name], 
    [Project1].[C1] AS [C1], 
    [Project1].[ProductID] AS [ProductID], 
    [Project1].[Name1] AS [Name1], 
    [Project1].[UnitsInStock] AS [UnitsInStock], 
    [Project1].[CategoryID1] AS [CategoryID1], 
FROM 
    (SELECT 
         [Extent1].[CategoryID] AS [CategoryID], 
         [Extent1].[Name] AS [Name], 
         [Extent2].[ProductID] AS [ProductID], 
         [Extent2].[Name] AS [Name1], 
         [Extent2].[UnitsInStock] AS [UnitsInStock], 
         [Extent2].[CategoryID] AS [CategoryID1], 
         CASE WHEN ([Extent2].[ProductID] IS NULL) 
            THEN CAST(NULL AS int) ELSE 1 END AS [C1]
     FROM  
         [dbo].[Categories] AS [Extent1]
     LEFT OUTER JOIN  
         [dbo].[Products] AS [Extent2] ON [Extent1].[CategoryID] = [Extent2].[CategoryID])  AS [Project1]
ORDER BY 
    [Project1].[CategoryID] ASC, [Project1].[C1] ASC

据我了解,当我使用.ToList()(急切加载)时,它应该看起来像这样,但是当我使用(默认)延迟加载时,它应该发送许多 sql 请求,分别询问 foreach 循环的所有元素。我的问题是 - 为什么没有区别并且总是只发送一个 SQL?

【问题讨论】:

【参考方案1】:

...当我使用 .ToList() 时(急切加载) ...当我使用(默认)延迟加载时

您混淆了两个不同的概念。使用ToList()eager loading 不同,lazy loading 则相反。它是强制执行,与延迟执行对应。

因此,使用或不使用ToList() 永远不会确定 LINQ 查询运行时 EF 将生成的 SQL 查询的数量。 Entity Framework 6(您的版本)总是尝试将 LINQ 查询转换为一个 SQL 语句。你有一个 LINQ 语句,因此你会得到一个 SQL 语句。

各种加载策略中的“加载”始终与填充导航属性有关。急切加载由Include 方法执行。例如:

var query = 
    from category in db.Categories
        .Include(c => c.Products)
    select category;

这会返回加载了 Products 导航属性的类别。

通过访问已执行的 LINQ 查询结果中的导航属性触发延迟加载。例如:

var query = db.Categories.ToList();

foreach (var c in query)

    Console.WriteLine("* 0", c.categoryName);

    foreach (var p in c.Products) // <= new query triggered here.
    
         Console.WriteLine("   - 0", p.Name);
    
;

为了发生延迟加载,导航属性应定义为virtual

class Category

    public int CategoryID  get; set; 
    public String Name  get; set; 
    public virtual ICollection<Product> Products  get; set; 

但通常,急切加载比延迟加载更受欢迎,因为它对数据库来说不太“健谈”。

【讨论】:

非常感谢您的澄清。显然和我一起讲课的那个人并没有很好地区分这个概念,但幸运的是有像你这样的人:)

以上是关于C# Entity Framework 中的 SQL 请求取决于延迟加载或急切加载的主要内容,如果未能解决你的问题,请参考以下文章

请教entity framework中数据更新的问题

使用带有 C# 的 Entity Framework 6 调用现有的存储过程

如何使用 Oracle 和 SQL Server 将 .NET 4.5 C# Entity Framework 6 中的列映射为大写?

Entity Framework Core 2 中的 ConnectionString 生成器

Entity Framework 4.1 中的性能监控选项

C# Entity-Framework:如何在模型对象上组合 .Find 和 .Include?