2015-04-27 2 views
1

Я задавался вопрос о следующих вещах:LINQ: один «где» положение по сравнению с множественной цепью «где положение»

Я могу запросить мою базу данных с помощью LINQ к организациям, как это:

GetAll().Where(
     x => x.SomeProperty == 'Yes' 
     && x.SomeOtherProperty == 'No') 
.ToList(); 

While Я вижу, некоторые из моих коллег изменить эти два Where-положения, например:

GetAll().Where(x => x.SomeProperty == 'Yes') 
     .Where(x.SomeOtherProperty == 'No') 
.ToList(); 

Оба запроса должны производить тот же результат, но мне было интересно, если один из двух имеет какие-либо преимущества/недостатки. Например, делает ли один метод более медленный запрос базы данных, или они будут генерировать тот же SQL-запрос?

+5

ориентир и дайте нам знать? –

+2

Вы можете посмотреть запросы и сами убедиться, добавив обработчик для 'context.Database.Log'. –

+3

Возможный дубликат [Proper Linq where clauses] (http://stackoverflow.com/questions/6359980/proper-linq-where-clauses) – Jonesopolis

ответ

2

Я установил тестовый проект, используя модель и контекст, описанные в 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]) 

Таким образом, кажется (по крайней мере, для простых случаев, как это), нет существенной разницы в производительности, так или иначе.Я думаю, вы могли бы утверждать, что для просмотра вашего дерева требуется некоторое время, чтобы изучить ваше дерево, если вы используете множественный подход Where, но в конечном итоге это ничтожно мало.

0

Я создал следующий тестовый пример в 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$1$0000 
IL_0021: br.s  IL_0023 
IL_0023: ldloc.0  // CS$1$0000 
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$1$0000 
IL_000E: br.s  IL_0010 
IL_0010: ldloc.0  // CS$1$0000 
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$1$0000 
IL_000E: br.s  IL_0010 
IL_0010: ldloc.0  // CS$1$0000 
IL_0011: ret   

Предложение Where, который использует && это первый бит IL, показывая два вызова Contains в анонимном методе. Тест, который использует предложение double Where, вызывает два разных анонимных метода, однако они кажутся одинаковыми, за исключением параметра, переданного в Contains. Рассматривая основной метод, мы можем видеть сделанные вызовы и видеть, что накладные расходы минимальны. Это приводит к чуть более медленному коду при использовании двух статей Where.

Мое предложение - запустить тест (например, кодированный выше) против фактической базы данных SQL и посмотреть, как выглядит производительность. Сгенерированный ИЛ может быть почти идентичным, но при переходе на SQL может существенно отличаться.

+1

Эти тесты LINQ to objects бессмысленны в контексте того, насколько эффективен SQL, сгенерированный провайдером LINQ to entity. Для всех, кого мы знаем, тот же SQL может быть сгенерирован Entity Framework для этих двух деревьев выражений. –