LINQ是广度优先还是深度优先?

时间:2013-11-22 18:17:53

标签: .net linq sql-execution-plan

LINQ是执行广度优先还是深度优先?

例如,请考虑以下代码段:

var stories = syndicationFeed.Items
  .Select ( item => ConstructStory ( item ) )
  .Where ( story => story != null )
  .OrderByDescending ( story => story.WhenPublished )
  .ToList();

在应用where子句之前是否完整执行了select子句(对于order子句也是如此)?或者是在尝试计算下一个对象之前计算的最终列表中的第一个对象?

5 个答案:

答案 0 :(得分:2)

  

“在应用where子句之前,是否完整执行了select子句”

如果您正在使用LinqToEntites(IQueyable个对象),则由驱动程序与数据源进行通信以确定顺序。

如果您正在使用LinqToObjects,它会尽可能地管理流程,因此 在第一个对象之前对每个对象执行Select检查过滤器后,在 选择评估期间检查过滤器 。您的代码大致相当于。

List<Story> YourCode(Feed syndicationFeed)
{
    IEnumerable<Story> storiesAsIEnumrable = syndicationFeed.EquivlantFunctionUpToToList()
    var stories = storiesAsIEnumrable.ToList(storiesAsIEnumrable);

    return stories;
}

IEnumerable<Story> EquivlantFunctionUpToToList(Feed feed)
{
    List<Story> storyBufferForSorting = new List<Story>();
    foreach(item in feed.Items)
    {
        var story = ConstructStory(item);
        if(story != null)
        {
            storyBufferForSorting.Add(story);
        }
    }
    storyBufferForSorting.OrderBy((x,y) => y.WhenPublished.CompareTo(x.WhenPublushed));
    return storyBufferForSorting;
}

//Taken right from the .NET framework source code via ILSpy
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    return new List<TSource>(source);
}

所以是的,项目在排序之前得到完全枚举(它必须能够确定哪个项目是“第一个”),并且再次发生完整的枚举以在ToList()中创建列表,但是过滤器在第一个枚举过程中发生,而不是额外的过程。

答案 1 :(得分:0)

如果syndicationFeed.ItemsIQueryable,则将通过组合各种方法来构建查询,直到实际请求数据为止(在这种情况下,通过调用ToList())。

但是,这实际上取决于您查询的数据类型以及您希望对其执行的操作。

答案 2 :(得分:0)

我认为没有明确的答案(假设您谈到“Linq to Entities”)。我相信这取决于“Linq to Entities”驱动程序,该驱动程序在SQL查询中转换“Linq to Entities”查询。然后,它取决于SQL Server(或任何其他数据库服务器)处理特定查询的方式。

如果您真的非常关心性能,我建议您分析特定查询。使用SQL Server,您可以查看执行计划。也许这会回答你的问题。查看SQL server profilerSQL Server execution planning

希望我帮忙!

答案 3 :(得分:0)

你说你指的是LinqToObjects(使用Enumarble(of T)上的扩展方法)。

大多数LinqToObjects方法在实现中使用yield关键字,而Select是其中之一。 这意味着它“选择”所有项目,然后继续进行下一个操作,它会逐个执行。

答案 4 :(得分:0)

Linq(对象)方法可以是流式传输非流式传输。有一个完整的概述here

大多数情况可以通过常识来理解。 Select可以将其数据流式传输到后续的LINQ方法,因为它可以“触发并忘记”单个项目。另一方面,OrderBy显然需要知道所有元素才能做任何事情。所以Select是流媒体,OrderBy不是。同样,Where正在流式传输,因为它同样只需要一次处理一个元素。

您可以通过将trace语句放入作为LINQ方法参数的委托中来检查这一点。我做了一个简单的测试,我碰巧碰到了某些东西。在Linqpad:

Crops.Select (c => { ("s-" + c.CrpId).Dump(); return c.CrpId;})
    .Where(i => { ("w1-" + i).Dump(); return i > 120; })
    .Where(i => { ("w2-" + i).Dump(); return i > 150; })
    .OrderBy (i => { ("o-" + i).Dump(); return i; })

如您所见,lambda表达式被方法体替换,这基本上没有任何区别。输出是:

s-21
w1-21
s-93
w1-93
...
s-122
w1-122
w2-122
s-123
w1-123
w2-123
...
s-158
w1-158
w2-158
o-155
o-157
o-158

因此,您会看到WhereSelect已进行流式传输,OrderBy会等待所有项目都可用。这也导致第二个Select(这里有点做作,OK)只在必要时执行。