Respuesta corta
Usted debe hacer lo que sientes es más legible y fácil de mantener en su aplicación ya que ambos evaluar a la misma colección.
Respuesta largabastante largo
LINQ to Objects
ATable.Where(x=> condition1 && condition2 && condition3)
Para este ejemplo Dado que sólo hay una declaración predicado el compilador sólo se necesita generar un delegado y un método compilador genera .
De reflector
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>();
El método generado por el compilador:
[CompilerGenerated]
private static bool <Main>b__0(ATable m)
{
return ((m.Prop1 && m.Prop2) && m.Prop3);
}
Como se puede ver que sólo hay una llamada en Enumerable.Where<T>
con el delegado como se esperaba, ya que sólo había un método Where
extensión .
ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
ahora para este ejemplo se genera mucho más código.
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>();
Como tenemos tres métodos de extensión de cadena que también consigue tres Func<T>
s y también métodos generados tres compilador.
[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;
}
Ahora, esto parece ser más lento, ya que hay un código de más. Sin embargo, dado que toda ejecución se difiere hasta que se llame al GetEnumerator()
, dudo que se presente una diferencia notable.
Algunos errores que podrían afectar el rendimiento
- Cualquier llamada a GetEnumerator en la cadena provocará una la colección a ser reiterado.
ATable.Where().ToList().Where().ToList()
se traducirá en una iteración de la colección con la primera predicado cuando el ToList
se llama y luego otra iteración con el segundo ToList
. Intente mantener el GetEnumerator llamado hasta el último momento para reducir el número de repeticiones de la colección.
LINQ a las entidades
Ya que estamos usando ahora nuestra IQueryable<T>
código compilador genera es un poco diferente, ya que estamos utilizando Expresssion<Func<T, bool>>
en lugar de nuestra normales Ejemplo Func<T, bool>
en todo en uno.
var allInOneWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix" && m.Id == 10 && m.GenreType_Value == 3);
Esto genera una gran declaración.
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 }));
El más notable es que nos encontramos con un árbol de expresión que se analiza a Expression.AndAlso
piezas. Y también como la espera sólo tenemos una llamada a Queryable.Where
var chainedWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix").Where(m => m.Id == 10).Where(m => m.GenreType_Value == 3);
siquiera me planteo molesto pegar en el código del compilador para esta, manera de largo. Pero, en resumen, terminamos con tres llamadas al Queryable.Where(Queryable.Where(Queryable.Where()))
y tres expresiones. De nuevo, esto se espera ya que tenemos tres cláusulas Where
encadenadas.
SQL generado
Como IEnumerable<T>
IQueryable<T>
también no se ejecuta hasta que el empadronador se llama. Debido a esto podemos estar feliz de saber que ambos producen la misma instrucción SQL exacta:
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])
Algunos errores que podrían afectar el rendimiento
- Cualquier llamada a GetEnumerator en la cadena hará que una llamada a sql, por ejemplo
ATable.Where().ToList().Where()
realmente consultará sql para todos los registros que coincidan con el primer predicado y luego filtrará la lista con linq a los objetos con el segundo predicado.
- Como mencionas extraer los predicados para usar else donde, haz seguro tienen el formato
Expression<Func<T, bool>>
y no simplemente Func<T, bool>
. El primero se puede analizar en un árbol de expresiones y se puede convertir en un sql válido; el segundo activará TODOS LOS OBJETOS devueltos y el Func<T, bool>
se ejecutará en esa colección.
Espero que haya sido un poco útil para responder a su pregunta.
¡gracias por la respuesta en profundidad! :) – Joe
+1 para una respuesta excelente –
+1 gracias por una respuesta increíble –