LINQ:一个 "where" 子句与多个链式 "where clauses"
LINQ: One "where" clause versus multiple chained "where clauses"
我在想接下来的事情:
我可以使用 LINQ To Entities 查询我的数据库,如下所示:
GetAll().Where(
x => x.SomeProperty == 'Yes'
&& x.SomeOtherProperty == 'No')
.ToList();
虽然我看到我的一些同事更改了这两个 WHERE 子句,如下所示:
GetAll().Where(x => x.SomeProperty == 'Yes')
.Where(x.SomeOtherProperty == 'No')
.ToList();
两个查询应该产生相同的输出,但我想知道两者之一是否有 advantages/disadvantages。例如,一种方法会产生较慢的数据库查询,还是会产生相同的 sql-query?
我在 LINQPad 中创建了以下测试用例。
void Main()
{
var listOfObjects = Enumerable.Range(0, 100000).Select(x => new TestClass() { SomeProperty = x.ToString() }).ToArray();
var iterations = DateTime.UtcNow.Day * 50;
Console.WriteLine("Doing {0} iterations", iterations);
var sw = Stopwatch.StartNew();
for(int i = 0; i < iterations; i++)
{
var filteredList = listOfObjects.Where(x => x.SomeProperty.Contains('0') && x.SomeProperty.Contains('1')).ToArray();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for(int i = 0; i < iterations; i++)
{
var filteredList = listOfObjects.Where(x => x.SomeProperty.Contains('0')).Where(x => x.SomeProperty.Contains('1')).ToArray();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public class TestClass
{
public string SomeProperty { get; set; }
public string SomeOtherProperty { get; set; }
}
迭代 1350 次后得到以下结果。
- 对于
&&
子句,19415 毫秒
- 对于两个
Where
子句,19596 毫秒
这似乎表明第一个要快一点。这可能只是编译器在幕后做了一些魔术,但是通过计算 TestClass
的列表以及 运行 时间的迭代次数,它不应该做任何事情。
为了确保与实际 SQL 一致,您应该看一下 IL,但根据我的看来,有效代码似乎是相同的。
<Main>b__2:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 30
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: brfalse.s IL_001E
IL_000F: ldarg.0
IL_0010: callvirt UserQuery+TestClass.get_SomeProperty
IL_0015: ldc.i4.s 31
IL_0017: call System.Linq.Enumerable.Contains
IL_001C: br.s IL_001F
IL_001E: ldc.i4.0
IL_001F: nop
IL_0020: stloc.0 // CS[=11=]00
IL_0021: br.s IL_0023
IL_0023: ldloc.0 // CS[=11=]00
IL_0024: ret
<Main>b__3:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 30
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: stloc.0 // CS[=11=]00
IL_000E: br.s IL_0010
IL_0010: ldloc.0 // CS[=11=]00
IL_0011: ret
<Main>b__4:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 31
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: stloc.0 // CS[=11=]00
IL_000E: br.s IL_0010
IL_0010: ldloc.0 // CS[=11=]00
IL_0011: ret
使用&&
的Where
子句是IL的第一位,显示了在匿名方法中对Contains
的两次调用。使用双 Where
子句的测试调用两个不同的匿名方法,但是它们看起来是相同的,除了传递给 Contains
的参数。查看 main 方法,我们可以看到进行的调用,并且开销很小。当使用两个 Where
子句时,它会导致代码稍慢。
我的建议是 运行 针对实际的 SQL 数据库进行测试(就像我在上面编码的那样),看看性能如何。生成的 IL 可能几乎相同,但在转换为 SQL.
时可能会有显着差异
我使用 this article 中描述的模型和上下文设置了一个测试项目,并按照您问题中的模式记录了两个不同查询的 SQL。我提出的查询是:
db.Blogs
.Where(b => b.BlogId == 0)
.Where(b => b.Name == "Foo");
和
db.Blogs
.Where(b => b.BlogId == 0 && b.Name == "Foo");
为两个查询生成的 SQL 是相同的:
SELECT
[Extent1].[BlogId] AS [BlogId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Blogs] AS [Extent1]
WHERE (0 = [Extent1].[BlogId]) AND (N'Foo' = [Extent1].[Name])
所以看起来(至少对于像这样的简单情况),无论哪种方式都没有显着的性能差异。我猜你可能会争辩说,如果你使用 multiple Where
方法,表达式 visitor 会花费更长的时间来检查你的树,但这在长 运行.
中可以忽略不计
我在想接下来的事情:
我可以使用 LINQ To Entities 查询我的数据库,如下所示:
GetAll().Where(
x => x.SomeProperty == 'Yes'
&& x.SomeOtherProperty == 'No')
.ToList();
虽然我看到我的一些同事更改了这两个 WHERE 子句,如下所示:
GetAll().Where(x => x.SomeProperty == 'Yes')
.Where(x.SomeOtherProperty == 'No')
.ToList();
两个查询应该产生相同的输出,但我想知道两者之一是否有 advantages/disadvantages。例如,一种方法会产生较慢的数据库查询,还是会产生相同的 sql-query?
我在 LINQPad 中创建了以下测试用例。
void Main()
{
var listOfObjects = Enumerable.Range(0, 100000).Select(x => new TestClass() { SomeProperty = x.ToString() }).ToArray();
var iterations = DateTime.UtcNow.Day * 50;
Console.WriteLine("Doing {0} iterations", iterations);
var sw = Stopwatch.StartNew();
for(int i = 0; i < iterations; i++)
{
var filteredList = listOfObjects.Where(x => x.SomeProperty.Contains('0') && x.SomeProperty.Contains('1')).ToArray();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for(int i = 0; i < iterations; i++)
{
var filteredList = listOfObjects.Where(x => x.SomeProperty.Contains('0')).Where(x => x.SomeProperty.Contains('1')).ToArray();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public class TestClass
{
public string SomeProperty { get; set; }
public string SomeOtherProperty { get; set; }
}
迭代 1350 次后得到以下结果。
- 对于
&&
子句,19415 毫秒 - 对于两个
Where
子句,19596 毫秒
这似乎表明第一个要快一点。这可能只是编译器在幕后做了一些魔术,但是通过计算 TestClass
的列表以及 运行 时间的迭代次数,它不应该做任何事情。
为了确保与实际 SQL 一致,您应该看一下 IL,但根据我的看来,有效代码似乎是相同的。
<Main>b__2:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 30
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: brfalse.s IL_001E
IL_000F: ldarg.0
IL_0010: callvirt UserQuery+TestClass.get_SomeProperty
IL_0015: ldc.i4.s 31
IL_0017: call System.Linq.Enumerable.Contains
IL_001C: br.s IL_001F
IL_001E: ldc.i4.0
IL_001F: nop
IL_0020: stloc.0 // CS[=11=]00
IL_0021: br.s IL_0023
IL_0023: ldloc.0 // CS[=11=]00
IL_0024: ret
<Main>b__3:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 30
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: stloc.0 // CS[=11=]00
IL_000E: br.s IL_0010
IL_0010: ldloc.0 // CS[=11=]00
IL_0011: ret
<Main>b__4:
IL_0000: ldarg.0
IL_0001: callvirt UserQuery+TestClass.get_SomeProperty
IL_0006: ldc.i4.s 31
IL_0008: call System.Linq.Enumerable.Contains
IL_000D: stloc.0 // CS[=11=]00
IL_000E: br.s IL_0010
IL_0010: ldloc.0 // CS[=11=]00
IL_0011: ret
使用&&
的Where
子句是IL的第一位,显示了在匿名方法中对Contains
的两次调用。使用双 Where
子句的测试调用两个不同的匿名方法,但是它们看起来是相同的,除了传递给 Contains
的参数。查看 main 方法,我们可以看到进行的调用,并且开销很小。当使用两个 Where
子句时,它会导致代码稍慢。
我的建议是 运行 针对实际的 SQL 数据库进行测试(就像我在上面编码的那样),看看性能如何。生成的 IL 可能几乎相同,但在转换为 SQL.
时可能会有显着差异我使用 this article 中描述的模型和上下文设置了一个测试项目,并按照您问题中的模式记录了两个不同查询的 SQL。我提出的查询是:
db.Blogs
.Where(b => b.BlogId == 0)
.Where(b => b.Name == "Foo");
和
db.Blogs
.Where(b => b.BlogId == 0 && b.Name == "Foo");
为两个查询生成的 SQL 是相同的:
SELECT
[Extent1].[BlogId] AS [BlogId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Blogs] AS [Extent1]
WHERE (0 = [Extent1].[BlogId]) AND (N'Foo' = [Extent1].[Name])
所以看起来(至少对于像这样的简单情况),无论哪种方式都没有显着的性能差异。我猜你可能会争辩说,如果你使用 multiple Where
方法,表达式 visitor 会花费更长的时间来检查你的树,但这在长 运行.