【问题标题】:linq styling, chaining where clause vs and operatorlinq 样式,链接 where 子句与和运算符
【发布时间】:2011-03-04 21:56:36
【问题描述】:

写作是否有(逻辑/性能)差异:

ATable.Where(x=> condition1 && condition2 && condition3)

ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)

我一直在使用前者,但意识到使用后者,我可以读取和复制部分查询,以便在其他地方更轻松地使用。 有什么想法吗?

【问题讨论】:

    标签: linq language-agnostic coding-style


    【解决方案1】:

    简答
    您应该在您的应用程序中执行您认为更具可读性和可维护性的操作,因为两者都将评估为同一个集合。

    长答案 很长

    Linq 到对象
    ATable.Where(x=> condition1 && condition2 && condition3) 对于这个例子,由于只有一个谓词语句,编译器只需要生成一个委托和一个编译器生成的方法。
    来自反射器

    if (CS$<>9__CachedAnonymousMethodDelegate4 == null)
    {
        CS$<>9__CachedAnonymousMethodDelegate4 = new Func<ATable, bool>(null, (IntPtr) <Main>b__0);
    }
    Enumerable.Where<ATable>(tables, CS$<>9__CachedAnonymousMethodDelegate4).ToList<ATable>();
    

    编译器生成方法:

    [CompilerGenerated]
    private static bool <Main>b__0(ATable m)
    {
        return ((m.Prop1 && m.Prop2) && m.Prop3);
    }
    

    如您所见,由于只有一个 Where 扩展方法,因此只有一次调用 Enumerable.Where&lt;T&gt; 与预期的委托一样。


    ATable.Where(x=&gt;condition1).Where(x=&gt;condition2).Where(x=&gt;condition3) 现在为这个示例生成了更多代码。

        if (CS$<>9__CachedAnonymousMethodDelegate5 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate5 = new Func<ATable, bool>(null, (IntPtr) <Main>b__1);
        }
        if (CS$<>9__CachedAnonymousMethodDelegate6 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate6 = new Func<ATable, bool>(null, (IntPtr) <Main>b__2);
        }
        if (CS$<>9__CachedAnonymousMethodDelegate7 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate7 = new Func<ATable, bool>(null, (IntPtr) <Main>b__3);
        }
        Enumerable.Where<ATable>(Enumerable.Where<ATable>(Enumerable.Where<ATable>(tables, CS$<>9__CachedAnonymousMethodDelegate5), CS$<>9__CachedAnonymousMethodDelegate6), CS$<>9__CachedAnonymousMethodDelegate7).ToList<ATable>();
    

    由于我们有三个链式扩展方法,因此我们还获得了三个 Func&lt;T&gt;s 以及三个编译器生成的方法。

    [CompilerGenerated]
    private static bool <Main>b__1(ATable m)
    {
        return m.Prop1;
    }
    
    [CompilerGenerated]
    private static bool <Main>b__2(ATable m)
    {
        return m.Prop2;
    }
    
    [CompilerGenerated]
    private static bool <Main>b__3(ATable m)
    {
        return m.Prop3;
    }
    

    现在看起来这应该会慢一些,因为还有大量的代码。然而,由于所有执行都被推迟到GetEnumerator() 被调用,我怀疑任何明显的差异都会出现。

    可能影响性能的一些陷阱

    • 在链中对 GetEnumerator 的任何调用都会导致集合被迭代。 ATable.Where().ToList().Where().ToList() 将导致在调用 ToList 时使用第一个谓词对集合进行迭代,然后使用第二个 ToList 进行另一次迭代。尽量将 GetEnumerator 调用保持在最后一刻,以减少迭代集合的次数。

    Linq To 实体
    由于我们使用的是IQueryable&lt;T&gt;,现在我们的编译器生成的代码有点不同,因为我们使用的是Expresssion&lt;Func&lt;T, bool&gt;&gt;,而不是我们正常的Func&lt;T, bool&gt;

    示例合二为一。
    var allInOneWhere = entityFrameworkEntities.MovieSets.Where(m =&gt; m.Name == "The Matrix" &amp;&amp; m.Id == 10 &amp;&amp; m.GenreType_Value == 3);

    这会产生一个非常糟糕的陈述。

    IQueryable&lt;MovieSet&gt; allInOneWhere = Queryable.Where&lt;MovieSet&gt;(entityFrameworkEntities.MovieSets, Expression.Lambda&lt;Func&lt;MovieSet, bool&gt;&gt;(Expression.AndAlso(Expression.AndAlso(Expression.Equal(Expression.Property(CS$0$0000 = Expression.Parameter(typeof(MovieSet), "m"), (MethodInfo) methodof(MovieSet.get_Name)), ..tons more stuff...ParameterExpression[] { CS$0$0000 }));

    最值得注意的是,我们最终得到了一个表达式树,它被解析为Expression.AndAlso 个片段。而且正如预期的那样,我们只有一个电话给Queryable.Where

    var chainedWhere = entityFrameworkEntities.MovieSets.Where(m =&gt; m.Name == "The Matrix").Where(m =&gt; m.Id == 10).Where(m =&gt; m.GenreType_Value == 3);

    我什至不会为此费心粘贴编译器代码,太长了。但简而言之,我们最终得到了对Queryable.Where(Queryable.Where(Queryable.Where())) 的三个调用和三个表达式。这也是意料之中的,因为我们有三个链接的Where 子句。

    生成的 Sql
    IEnumerable&lt;T&gt; IQueryable&lt;T&gt; 一样,在调用枚举器之前也不会执行。正因为如此,我们很高兴知道两者都产生了完全相同的 sql 语句:

    SELECT 
    [Extent1].[AtStore_Id] AS [AtStore_Id], 
    [Extent1].[GenreType_Value] AS [GenreType_Value], 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name]
    FROM [dbo].[MovieSet] AS [Extent1]
    WHERE (N'The Matrix' = [Extent1].[Name]) AND (10 = [Extent1].[Id]) AND (3 = [Extent1].[GenreType_Value])
    

    可能影响性能的一些陷阱

    • 对链中 GetEnumerator 的任何调用都会导致对 sql 的调用,例如ATable.Where().ToList().Where() 实际上会在 sql 中查询与第一个谓词匹配的所有记录,然后使用 linq 将列表过滤为具有第二个谓词的对象。
    • 既然您提到提取谓词以在 else where 中使用,请确保 确保它们采用 Expression&lt;Func&lt;T, bool&gt;&gt; 而不仅仅是 Func&lt;T, bool&gt; 的形式。第一个可以解析为表达式树并转换为有效的 sql,第二个将触发 ALL OBJECTS 返回,Func&lt;T, bool&gt; 将在该集合上执行。

    我希望这对回答你的问题有点帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-06
      • 2015-06-12
      相关资源
      最近更新 更多