一对多投影的LINQ查询重复执行

时间:2013-04-26 15:45:00

标签: c# performance linq-to-sql

我正在将LINQ to SQL结果投射到强类型类:Parent和Child。这两个查询之间的性能差异很大:

慢速查询 - 从DataContext进行日志记录显示正在为每个父进程单独调用数据库

var q = from p in parenttable
        select new Parent()
        {
            id = p.id,
            Children = (from c in childtable
                        where c.parentid = p.id
                        select c).ToList()
        }
return q.ToList()  //SLOW

快速查询 - 从DataContext进行日志记录显示单个数据库命中查询,该查询返回所有必需数据

var q = from p in parenttable
        select new Parent()
        {
            id = p.id,
            Children = from c in childtable
                        where c.parentid = p.id
                        select c
        }
return q.ToList()  //FAST

我想强制LINQ使用第二个示例的单一查询样式,但直接用Parent对象填充Parent类。否则,Children属性是IQuerierable<Child>,必须查询它以暴露Child对象。

引用的问题似乎并不能解决我的情况。使用db.LoadOptions不起作用。也许它要求类型是在DataContext中注册的TEntity。

   DataLoadOptions options = new DataLoadOptions();
   options.LoadWith<Parent>(p => p.Children);
   db.LoadOptions = options;

请注意:父和子是简单类型,而不是Table<TEntity>类型。父母与子女之间没有语境关系。子查询是临时的。

问题的症结:在第二个LINQ示例中,我实现了IQueriable语句,并且不调用ToList()函数,由于某种原因,LINQ知道如何生成一个可以检索所有必需的查询数据。如何使用第一个查询中完成的实际数据填充我的临时投影?此外,如果有人能帮助我更好地表达我的问题,我将不胜感激。

4 个答案:

答案 0 :(得分:6)

重要的是要记住LINQ查询依赖于延迟执行。在第二个查询中,您实际上并未获取有关子项的任何信息。您已经创建了查询,但实际上并没有执行它们来获取这些查询的结果。如果您要迭代列表,然后迭代每个项目的Children集合,您会看到它花费的时间与第一个查询一样多。

您的查询本身也非常低效。您正在使用嵌套查询来表示Join关系。如果您使用Join,则查询提供程序和数据库都可以适当地优化查询,以便更快地执行。您可能还需要调整数据库上的索引以提高性能。以下是连接的外观:

var q = from p in parenttable
        join child in childtable
        on p.id equals child.parentid into children
        select new Parent()
        {
            id = p.id,
            Children = children.ToList(),
        }
return q.ToList()  //SLOW

答案 1 :(得分:1)

我发现完成此任务的最快方法是执行一个返回所有结果的查询,然后对所有结果进行分组。确保在第一个查询中执行.ToList(),以便第二个查询不会进行多次调用。

这里r应该只有一个数据库查询就可以实现。

            var q = from p in parenttable
                    join c in childtable on p.id equals c.parentid
                    select c).ToList();
            var r = q.GroupBy(x => x.parentid).Select(x => new { id = x.Key, Children=x });

答案 2 :(得分:0)

您必须为数据加载设置正确的选项。

options.LoadWith<Document>(d => d.Metadata);

查看this

P.S。 Include仅限LINQToEntity

答案 3 :(得分:0)

第二个查询快速正是因为未填充子项。

第一个是慢只是因为正在填充儿童。

选择最适合您需求的产品,您根本无法将它们的功能放在一起!

编辑:

正如 @Servy 所说:

  

在您的第二个查询中,您实际上并未获取有关这些子项的任何信息。您已经创建了查询,但实际上并没有执行它们来获取这些查询的结果。如果您要迭代列表,然后迭代每个项目的Children集合,您会看到它花费的时间与第一个查询一样多。