linq 样式、链接 where 子句与 and 运算符

发布于 2024-10-19 23:32:51 字数 297 浏览 0 评论 0 原文

写入是否存在(逻辑/性能)差异:

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

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

我一直在使用前者,但意识到使用后者,我可以读取查询的部分内容并将其复制到在其他地方使用更容易。 有什么想法吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

云雾 2024-10-26 23:32:51

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

长答案 相当长

Linq To Objects
ATable.Where(x=>条件1 &&条件2 &&条件3)
对于此示例,由于只有一个谓词语句,因此编译器只需生成一个委托和一个编译器生成的方法。
来自反射器

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

编译器生成的方法:

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

如您所见,只有一次对 Enumerable.Where 的调用,如预期的那样,因为只有一个Where 扩展方法。

ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3) 现在,对于此示例,生成了更多代码。

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

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

[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,现在我们的编译器生成的代码有点不同,因为我们使用 Expression> 而不是正常的 < code>Func

示例集于一身。
var allInOneWhere =EntityFrameworkEntities.MovieSets.Where(m => m.Name == "黑客帝国" && m.Id == 10 && m.GenreType_Value == 3);

这会生成一个非常糟糕的声明。

IQueryable; allInOneWhere = Queryable.Where(entityFrameworkEntities.MovieSets, Expression.Lambda>(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 == "黑客帝国").Where(m => m.Id == 10).Where(m => m.GenreType_Value == 3);

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

生成的Sql
IEnumerable 一样,IQueryable 在调用枚举器之前也不会执行。因此,我们很高兴知道两者都会生成完全相同的 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 to object 过滤列表。
  • 既然您提到提取谓词以在其他地方使用,请确保它们采用 Expression> 的形式,而不仅仅是 FuncFunc< ;T,布尔>。第一个可以解析为表达式树并转换为有效的 sql,第二个将触发返回所有对象,并且 Func 将在该集合上执行。

我希望这对回答您的问题有所帮助。

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
lt;>9__CachedAnonymousMethodDelegate4 == null)
{
    CS
lt;>9__CachedAnonymousMethodDelegate4 = new Func<ATable, bool>(null, (IntPtr) <Main>b__0);
}
Enumerable.Where<ATable>(tables, CS
lt;>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
lt;>9__CachedAnonymousMethodDelegate5 == null)
    {
        CS
lt;>9__CachedAnonymousMethodDelegate5 = new Func<ATable, bool>(null, (IntPtr) <Main>b__1);
    }
    if (CS
lt;>9__CachedAnonymousMethodDelegate6 == null)
    {
        CS
lt;>9__CachedAnonymousMethodDelegate6 = new Func<ATable, bool>(null, (IntPtr) <Main>b__2);
    }
    if (CS
lt;>9__CachedAnonymousMethodDelegate7 == null)
    {
        CS
lt;>9__CachedAnonymousMethodDelegate7 = new Func<ATable, bool>(null, (IntPtr) <Main>b__3);
    }
    Enumerable.Where<ATable>(Enumerable.Where<ATable>(Enumerable.Where<ATable>(tables, CS
lt;>9__CachedAnonymousMethodDelegate5), CS
lt;>9__CachedAnonymousMethodDelegate6), CS
lt;>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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文