我可以或应该在LINQ查询中连接两个Where子句

本文关键字:两个 子句 Where 连接 查询 LINQ 我可以 | 更新日期: 2023-09-27 18:19:06

我有以下类:

public partial class Content
{
    public int ContentId { get; set; }
    public int ContentTypeId { get; set; }
    public string Title { get; set; }
    public string Text { get; set; }
    public int SubjectId { get; set; }
    public virtual Subject Subject { get; set; }
}

我明白我可以像这样使用Linq查询:

.Where(a => a.SubjectId == subjectId)

但是我怎样才能使它有另一个条件

.Where(a => a.ContentTypeId == contentTypId) 

是否有一种方法可以将它们合并为一个,或者它们应该保持两个?

我可以或应该在LINQ查询中连接两个Where子句

仅使用一个包含所有条件的Where子句:

.Where(a => a.SubjectId == subjectId && a.ContentTypeId == contentTypId)

或两个Where子句,各处理一个条件:

.Where(a => a.SubjectId == subjectId)
.Where(a => a.ContentTypeId == contentTypId)

是等价的,因为LINQ查询的执行被延迟到调用结果。

您还可以:

.Where(a => a.SubjectId == subjectId).Where(a => a.ContentTypeId == contentTypId) 
这对于在代码中动态构建查询非常有用。

omer schleifer的回答让我对这种情况进行基准测试,并最终检查创建的IL,看看是否在链接子句中有性能影响。

让我们看看下面的例子:

var numbers = new List<int>() { 1, 2 ,3,4,5,6,7,8,9,10};
IEnumerable<int> query = numbers.Where(x=> x>2 && x<5);

将导致以下IL:

IL_0001:  newobj      System.Collections.Generic.List<System.Int32>..ctor
IL_0006:  stloc.2     // <>g__initLocal0
IL_0007:  ldloc.2     // <>g__initLocal0
...
...
...    
IL_0059:  ldloc.2     // <>g__initLocal0
IL_005A:  stloc.0     // numbers
IL_005B:  ldloc.0     // numbers
IL_005C:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate2
IL_0061:  brtrue.s    IL_0076
IL_0063:  ldnull      
IL_0064:  ldftn       b__1
IL_006A:  newobj      System.Func<System.Int32,System.Boolean>..ctor
IL_006F:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate2
IL_0074:  br.s        IL_0076
IL_0076:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate2
IL_007B:  call        System.Linq.Enumerable.Where
IL_0080:  stloc.1     // query
IL_0081:  ldloc.1     // query    
b__1:
IL_0000:  ldarg.0     
IL_0001:  ldc.i4.2    
IL_0002:  ble.s       IL_000A
IL_0004:  ldarg.0     
IL_0005:  ldc.i4.5    
IL_0006:  clt         
IL_0008:  br.s        IL_000B
IL_000A:  ldc.i4.0    
IL_000B:  stloc.0     // CS$1$0000
IL_000C:  br.s        IL_000E
IL_000E:  ldloc.0     // CS$1$0000
IL_000F:  ret  

//链接示例:

var numbers = new List<int>() { 1, 2 ,3,4,5,6,7,8,9,10};
IEnumerable<int> query = numbers.Where(x=> x>2).Where(x => x<5);

//结果如下:

IL_0001:  newobj      System.Collections.Generic.List<System.Int32>..ctor
IL_0006:  stloc.2     // <>g__initLocal0
IL_0007:  ldloc.2     // <>g__initLocal0
IL_0008:  ldc.i4.1    
...
...
...
IL_0058:  nop         
IL_0059:  ldloc.2     // <>g__initLocal0
IL_005A:  stloc.0     // numbers
IL_005B:  ldloc.0     // numbers
IL_005C:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate3
IL_0061:  brtrue.s    IL_0076
IL_0063:  ldnull      
IL_0064:  ldftn       b__1
IL_006A:  newobj      System.Func<System.Int32,System.Boolean>..ctor
IL_006F:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate3
IL_0074:  br.s        IL_0076
IL_0076:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate3
IL_007B:  call        System.Linq.Enumerable.Where  <--------first where call
IL_0080:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate4
IL_0085:  brtrue.s    IL_009A
IL_0087:  ldnull      
IL_0088:  ldftn       b__2
IL_008E:  newobj      System.Func<System.Int32,System.Boolean>..ctor
IL_0093:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate4
IL_0098:  br.s        IL_009A
IL_009A:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate4
IL_009F:  call        System.Linq.Enumerable.Where  <--------second where call
IL_00A4:  stloc.1     // query
IL_00A5:  ldloc.1     // query
b__1:
IL_0000:  ldarg.0     
IL_0001:  ldc.i4.2    
IL_0002:  cgt         
IL_0004:  stloc.0     // CS$1$0000
IL_0005:  br.s        IL_0007
IL_0007:  ldloc.0     // CS$1$0000
IL_0008:  ret         
b__2:
IL_0000:  ldarg.0     
IL_0001:  ldc.i4.5    
IL_0002:  clt         
IL_0004:  stloc.0     // CS$1$0000
IL_0005:  br.s        IL_0007
IL_0007:  ldloc.0     // CS$1$0000
IL_0008:  ret

该示例显示有两个调用,第二个调用接收第一个结果作为输入。
所以在Linq to Objects中会有一个性能打击。性能恶化的程度将取决于数据量和where子句的顺序,第一个子句过滤的越多,下一个子句必须操作的就越少,等等……在我看来,在大多数情况下,性能影响不会很大。

Linq to SQL链Where子句将不会影响性能,因为将创建相同的SQL