本文介绍了linq style,chaining where clause vs和operator的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



ATable.Where(x => condition1&& condition2 &&&&&&&&&&&")3





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



我一直在使用前者,但意识到后者,我可以阅读和复制部分查询出来使用其他地方更容易。
任何想法?

解决方案



长回答 很长时间,你应该在应用程式中执行更多的可读性和可维护性长



Linq To Objects

ATable。其中(x => condition1&&& condition2&&&condition3)
对于此示例由于只有一个谓词语句,编译器将只需要生成一个代理和一个编译器生成的方法。

从反射器

  if (CS $<>><>> 9CachedAnonymousMethodDelegate4 == 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);
}

正如你可以看到,只有一个调用 Enumerable.Where< T> 与预期的委托,因为只有一个其中扩展方法。





ATable.Where(x => condition1).Where(x => condition2).Where(x => condition3)现在为这个例子生成了更多的代码。

  if(CS $<> 9_ChachedAnonymousMethodDelegate5 == 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>(表,CS $< 9__CachedAnonymousMethodDelegate5),CS $< 9__CachedAnonymousMethodDelegate6) CS $<> 9__CachedAnonymousMethodDelegate7).ToList< ATable>();

因为我们有三个链接的扩展方法,我们也得到三个 Func< T& 以及三种编译器生成的方法。

  [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;
}

现在看起来像这样应该更慢,因为heck还有一吨多的代码。但是因为所有的执行都被推迟到 GetEnumerator()被调用,我怀疑任何明显的差异将呈现。






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



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

全部在一个中的示例。

var allInOneWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == The Matrix&& m.Id == 10& m.GenreType_Value == 3);





IQueryable< MovieSet> allInOneWhere = Queryable.Where< MovieSet>(entityFrameworkEntities.MovieSets,Expression.Lambda< Func< MovieSet,bool>(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 => m.Name ==The Matrix)。其中(m => m.Id == 10).Where(m => m.GenreType_Value == 3);



我甚至不打算在编译器代码中粘贴这个方法,但是简而言之,我们结束了三次调用 Queryable.Where(Queryable.Where(Queryable.Where()))和三个表达式。这再次是预期的,因为我们有三个其中子句。



生成的Sql

IEnumerable< T> IQueryable< T> 称为。因为这个,我们可以很高兴地知道,两者都产生相同的精确sql语句:

  SELECT 
[Extent1] 。[AtStore_Id] AS [AtStore_Id],
[Extent1]。[GenreType_Value] AS [GenreType_Value],
[Extent1]。[Id] AS [Id],
[Extent1]名称] AS [名称]
从[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,在肯定中,它们的格式为 Expression< Func< T, bool> ,而不是简单地 Func< T,bool> 。第一个可以解析为表达式树并转换为有效的sql,第二个将触发 ALL OBJECTS 返回并且 Func


  • 我希望这有助于回答您的问题。


    Is there a (logical/performance) difference to writing:

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

    or

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

    I've been using the former but realised that with the latter, I can read and copy parts of a query out to use somewhere else easier.Any thoughts?

    解决方案

    Short answer
    You should do what you feel is more readable and maintainable in your application as both will evaluate to the same collection.

    Long answer quite long

    Linq To Objects
    ATable.Where(x=> condition1 && condition2 && condition3)For this example Since there is only one predicate statement the compiler will only needs to generate one delegate and one compiler generated method.
    From reflector

    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>();
    

    The compiler generated method:

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

    As you can see there is only one call into Enumerable.Where<T> with the delegate as expected since there was only one Where extension method.


    ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3) now for this example a lot more code is generated.

        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>();
    

    Since we have three chained Extension methods we also get three Func<T>s and also three compiler generated methods.

    [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;
    }
    

    Now this looks like this should be slower since heck there is a ton more code. However since all execution is deferred until GetEnumerator() is called I doubt any noticeable difference will present itself.

    Some Gotchas that could effect performance

    • Any call to GetEnumerator in the chain will cause a the collection to be iterated. ATable.Where().ToList().Where().ToList() will result in an iteration of the collection with the first predicate when the ToList is called and then another iteration with the second ToList. Try to keep the GetEnumerator called to the very last moment to reduce the number of times the collection is iterated.

    Linq To Entities
    Since we are using IQueryable<T> now our compiler generated code is a bit different as we are using Expresssion<Func<T, bool>> instead of our normal Func<T, bool>

    Example in all in one.
    var allInOneWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix" && m.Id == 10 && m.GenreType_Value == 3);

    This generates one heck of a statement.

    IQueryable<MovieSet> allInOneWhere = Queryable.Where<MovieSet>(entityFrameworkEntities.MovieSets, Expression.Lambda<Func<MovieSet, bool>>(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 }));

    The most notable is that we end up with one Expression tree that is parsed down to Expression.AndAlso pieces. And also like expected we only have one call to Queryable.Where

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

    I wont even bother pasting in the compiler code for this, way to long. But in short we end up with Three calls to Queryable.Where(Queryable.Where(Queryable.Where())) and three expressions. This again is expected as we have three chained Where clauses.

    Generated Sql
    Like IEnumerable<T> IQueryable<T> also does not execute until the enumerator is called. Because of this we can be happy to know that both produce the same exact sql statement:

    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])
    

    Some Gotchas that could effect performance

    • Any call to GetEnumerator in the chain will cause a call out to sql, e.g. ATable.Where().ToList().Where() will actually query sql for all records matching the first predicate and then filter the list with linq to objects with the second predicate.
    • Since you mention extracting the predicates to use else where, make sure they are in the form of Expression<Func<T, bool>> and not simply Func<T, bool>. The first can be parsed to an expression tree and converted into valid sql, the second will trigger ALL OBJECTS returned and the Func<T, bool> will execute on that collection.

    I hope this was a bit helpful to answer your question.

    这篇关于linq style,chaining where clause vs和operator的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-19 21:08