【问题标题】:Optimize SQL generated by LINQ Query in Entity Framework 4.1 with one-to-many associations使用一对多关联优化 Entity Framework 4.1 中 LINQ Query 生成的 SQL
【发布时间】:2011-09-23 13:43:55
【问题描述】:

我在使用 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

最大的问题是 Class A -> Class B 总是有超过 10k 行,而事实上,所有这些行都被加载到内存中,并且分页是在内存中进行的,但我希望此分页将由 SQL Server 完成。

关于如何实现这一点的任何想法?

【问题讨论】:

    标签: c# sql-server linq query-optimization entity-framework-4.1


    【解决方案1】:

    您必须区分 linq-to-entities 和 linq-to-objects。这个:

    var classA = (from x in db.ClassAs
                  where x.ID == 2
                  select x).First();
    

    是 linq-to-entities。您正在访问db.ClassAs,提供IQueryable 来构建表达式树,当您调用First() 时,它将在数据库中作为SQL 执行。但是这个:

    var classesB = (from b in classA.Children
                    select b.Data).Skip(10).Take(10);
    

    是 linq-to-objects。查询本身在集合 (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;
    

    【讨论】:

      猜你喜欢
      • 2012-10-20
      • 2011-09-17
      • 1970-01-01
      • 2011-11-03
      • 2015-08-08
      • 1970-01-01
      • 1970-01-01
      • 2011-11-11
      • 2011-12-05
      相关资源
      最近更新 更多