Краткий ответ Вы должны делать то, что, по вашему мнению, более читабельно и легко поддерживается в вашем приложении, поскольку оба будут оценивать одну и ту же коллекцию.
Длинный ответ довольно длинный
Линк к объектам ATable.Where(x=> condition1 && condition2 && condition3)
Для этого примера Поскольку существует только один предикатный оператор, компилятору потребуется только сгенерировать один делегат и один метод, сгенерированный компилятором. От отражателя
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>();
Метод, сгенерированный компилятором:
[CompilerGenerated]
private static bool <Main>b__0(ATable m)
{
return ((m.Prop1 && m.Prop2) && m.Prop3);
}
Как вы можете видеть, в Enumerable.Where<T>
есть только один вызов с делегатом, как и ожидалось, поскольку было только одно расширение Where
Метод.
ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
теперь для этого примера генерируется намного больше кода.
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>();
Поскольку у нас есть три цепных метода Extension, мы также получаем три Func<T>
s и три метода, сгенерированных компилятором.
[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 Entities Так как мы используем 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").Where(m => m.Id == 10).Where(m => m.GenreType_Value == 3);
Я даже не буду вставлять код компилятора для этого, вплоть до long.Но вкратце мы получаем три вызова на Queryable.Where(Queryable.Where(Queryable.Where()))
и три выражения.Это снова ожидается, поскольку у нас есть три цепочки 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].[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 для объектов со вторым предикатом. - Поскольку выупомяните извлечение предикатов для использования в другом месте, где убедитесь, что они имеют форму
Expression<Func<T, bool>>
, а не просто Func<T, bool>
.Первый может быть проанализирован в дереве выражений и преобразован в действительный sql, второй вызовет ВСЕ ОБЪЕКТЫ возвращено и Func<T, bool>
будет выполнено в этой коллекции.
Я надеюсьЭто было немного полезно, чтобы ответить на ваш вопрос.