使用一对多关联优化实体框架4.1中LINQ查询生成的SQL
本文关键字:查询 LINQ SQL 一对多 关联 优化 框架 实体 | 更新日期: 2023-09-27 18:00:40
我对LINQ生成的Sql查询有一些问题,由于我的环境很大,我做了一个简单的例子来反映我的问题。
这是我的型号:
public class ClassA
{
public int ID { get; set; }
public virtual ICollection<ClassB> Children { get; set; }
}
public class ClassB
{
public int ID { get; set; }
public string Data { get; set; }
}
public class ClassC
{
public int ID { get; set; }
public virtual ICollection<ClassB> Children { get; set; }
}
很简单吧?
这是我的问题:
var classA = (from x in db.ClassAs
where x.ID == 2
select x).First();
var classesB = (from b in classA.Children
select b.Data).Skip(10).Take(10);
classesB.ToList();
问题是当这个查询被翻译成SQL:时
(from x in db.ClassAs
where x.ID == 2
select x).First()
变为:
SELECT TOP (1)
[Extent1].[ID] AS [ID]
FROM [dbo].[ClassAs] AS [Extent1]
WHERE 2 = [Extent1].[ID]
和:
from b in classA.Children
select b.Data).Skip(10).Take(10)
变为:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[Data] AS [Data],
[Extent1].[ClassA_ID] AS [ClassA_ID]
FROM [dbo].[ClassBs] AS [Extent1]
WHERE ([Extent1].[ClassA_ID] IS NOT NULL) AND ([Extent1].[ClassA_ID] = @EntityKeyValue1)
我希望生成的查询是这样的:
SELECT [Data] AS [Data]
FROM (SELECT
[Data] AS [Data],
rownum = ROW_NUMBER() OVER (ORDER BY [B].[ID])
FROM ClassBs AS B , ClassAs AS A
WHERE B.ClassA_ID = A.ID
AND A.ID = 2) AS T1
WHERE [t1].rownum BETWEEN 11 AND 20
ORDER BY [t1].rownum
最大的问题是,类A->B总是有超过10k行,而且实际上,所有这些行都被加载到内存中,并且在内存中进行分页,但我希望这个分页由SQL Server完成。
有没有想过如何做到这一点?
linq到实体和linq到对象之间必须不同。此:
var classA = (from x in db.ClassAs
where x.ID == 2
select x).First();
是linq到实体。您正在访问提供IQueryable
的db.ClassAs
以构建表达式树,当您调用First()
时,表达式树将在数据库中作为SQL执行。但是这个:
var classesB = (from b in classA.Children
select b.Data).Skip(10).Take(10);
是对对象的linq。查询本身是在集合(HashSet
)上定义的,并在该集合上执行。因为您的属性被标记为virtual
EF将触发延迟加载,并填充该属性中的所有数据,以便执行内存中的查询。它永远不会以不同的方式工作。
如果您想对相关属性进行数据库查询,则必须使用显式加载而不是延迟加载:
db.Entry(classA)
.Collection(c => c.Children)
.Query()
.OrderBy(...) // You must order entities before you can use Skip and Take
.Skip(10)
.Take(10)
.Load();
var classesB = classA.Children;