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

SQL requests in C# Entity Framework depending on lazy or eager loading

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

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?

... when I use .ToList() (eager loading)
... when I use (default) lazy loading

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

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

"Loading" 在各种加载策略中始终与填充导航属性有关。预先加载是通过 Include 方法执行的。例如:

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

此 returns 类别及其 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; }
}

虽然通常情况下,预加载优于延迟加载,因为它对数据库的影响较小 "chatty"。