2010-10-28 16 views
6

Tuve una consulta LINQ que carga una jerarquía de objetos como la siguiente.EF Donde (x => x.ColumnVal == 1) vs FirstOrDefault (x => x.Column == 1)

consulta # 1

var result = db.Orders 
       .Include("Customer") 
       // many other .Include() here 
       .FirstOrDefault(x => x.Customer.CustomerId == 1 && 
            x.OrderId == orderId); 

que estaba teniendo problema de rendimiento MAYOR con él.
El uso de la CPU fue cercano al 100% y el uso de memoria fue muy alto.

Y lo pellizqué a lo siguiente y se solucionó el problema de rendimiento.

consulta # 2

var result = db.Orders 
       .Include("Customer") 
       // many other .Include() here 
       .Where(x => x.Customer.CustomerId == 1 && 
          x.OrderId == orderId) 
       .FirstOrDefault(); 



sólo quiero confirmar mi sospecha.
consulta # 1 es, probablemente, un bucle a través de todos mis registros en la memoria en busca de un registro coincidente
vs
consulta # 2 filtra los registros en la base de datos y luego conseguir el primer registro solamente.

¿Por eso la consulta # 1 tiene problemas de rendimiento?

Sólo para estar seguro, necesito para utilizar el .Select(x => x) antes de la .FirstOrDefault()?

consulta # 3

var result = db.Orders 
       .Include("Customer") 
       // many other .Include() here 
       .Where(x => x.Customer.CustomerId == 1 && 
          x.OrderId == orderId) 
       .Select(x => x) 
       .FirstOrDefault(); 
+0

Estoy de acuerdo con sus ideas iniciales. Observa las consultas generadas y deja tu mente de lado :-) –

Respuesta

3

No, ambos deben resultar en una misma consulta SQL cuando se está ejecutando. Puede probarlo consultando Analizador de SQL y vea cuál es el SQL exacto que se envía desde EF en ambos casos. Su optimización del rendimiento debería haber sido causada por otros factores. He aquí por qué:

Incluir método devuelve un ObjectQuery<T>:

public class ObjectQuery<T> : ObjectQuery, IOrderedQueryable<T>, 
           IQueryable<T>, IEnumerable<T>, 
           IOrderedQueryable, IQueryable, 
           IEnumerable, IListSource 

que significa que sus FirstOrDefault método viene con 2 sobrecargas:

// Defined by Enumerable: 
FirstOrDefault(Func<T, Boolean>) 

// Defined by Queryable: 
FirstOrDefault(Expression<Func<T, Boolean>>) 

Cuando código .FirstOrDefault(x => x.Customer.CustomerId == 1 compilador entrará en un proceso llamado Resolución de sobrecarga para inferir el tipo de la expresión lambda x => x.Customer.CustomerId == 1 ya que es convertible al tipo de ambos tipos de parámetros de sobrecarga.
El compilador utilizará un algoritmo (¡que todavía estoy tratando de encontrar en la especificación del lenguaje C#!), Darse cuenta de que la conversión de la lambda de la Expression<Func<T, Boolean> es un mejor conversión que a Func<T, Boolean> para recoger la IQueryable sobrecarga.
Por lo tanto, verá el predicado en el SQL generado al observarlo en el Analizador de SQL.

+0

Hola Morteza, respondiste una pregunta similar de forma un poco diferente, pero le pediste que incluyera "using System.Linq.Expressions;". No tengo esa declaración de uso. ¿Es por eso que estoy teniendo un problema de rendimiento? http://stackoverflow.com/questions/3540410/ef-4-0-weber-behaviour-of-firstordefault-method/3540477#3540477 – stun

+0

Gracias por recordar eso. Estoy de acuerdo, es diferente, pero todavía estoy insistiendo en mi nueva respuesta aquí y actualizo mi respuesta para mostrar por qué. Ejecute ambos y vea el resultado en el Analizador, verá que ambos dan como resultado exactamente el mismo SQL y, por favor, avíseme si no lo es. –

0

Encontré al culpable. Es la consulta SQL generada por Entity Framework.
Tengo un esquema complicado con muchas relaciones de muchos a muchos.

Entity Framework estaba generando un 32000 línea de cadena larga SQL:. '(
Ahora, voy a cambiar mi código para cargar la jerarquía manualmente por alguna parte

Por favor, hágamelo saber si alguien sabe alguna buena artículos para leer sobre Cargando Eager y muchos-a-muchas relaciones.

0

Creo que mejor sería usar ... ¿Dónde (condición) .Tomar (1) .FirstOrDefault() porque Take (1) puede ser traducido fácilmente a SQL como una cláusula TOP. ¿Alguien conmigo?

+0

FirstOrDefault() generará automáticamente la consulta con TOP de SQL (1) – Bertm13