2011-03-04 13 views
12

¿Hay un (rendimiento lógico /) diferencia a la escritura:estilo de LINQ, encadenando cláusula where vs y el operador

ATable.Where(x=> condition1 && condition2 && condition3)

o

ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)

He estado usando la antigua pero me di cuenta de que con este último, puedo leer y copiar partes de una consulta para usar en otro lugar más fácil. ¿Alguna idea?

Respuesta

22

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.

+2

¡gracias por la respuesta en profundidad! :) – Joe

+1

+1 para una respuesta excelente –

+1

+1 gracias por una respuesta increíble –

Cuestiones relacionadas