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"。
我有一个简单的连接查询,连接两个表(一个类别有很多产品):
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"。