2011-06-27 42 views
5

Tengo algunos problemas con la consulta SQL generada por LINQ, Como mi entorno es bastante grande, hice un ejemplo simple que refleja mi problema.Optimizar SQL generado por LINQ Query en Entity Framework 4.1 con asociaciones uno a muchos

Esta es mi modelo:

public class ClassA 
{ 
    public int ID { get; set; } 
    public virtual ICollection<ClassB> Children { get; set; } 
} 

public class ClassB 
{ 
    public int ID { get; set; } 
    public string Data { get; set; } 
} 

public class ClassC 
{ 
    public int ID { get; set; } 
    public virtual ICollection<ClassB> Children { get; set; } 
} 

Muy simple eh?

Bueno, esta es mi consulta:

var classA = (from x in db.ClassAs 
      where x.ID == 2 
      select x).First(); 
var classesB = (from b in classA.Children 
       select b.Data).Skip(10).Take(10); 

classesB.ToList(); 

El problema es cuando esta consulta se traduce a SQL:

(from x in db.ClassAs 
where x.ID == 2 
select x).First() 

se convierte en:

SELECT TOP (1) 
[Extent1].[ID] AS [ID] 
FROM [dbo].[ClassAs] AS [Extent1] 
WHERE 2 = [Extent1].[ID] 

y:

from b in classA.Children 
select b.Data).Skip(10).Take(10) 

se convierte en:

SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[Data] AS [Data], 
[Extent1].[ClassA_ID] AS [ClassA_ID] 
FROM [dbo].[ClassBs] AS [Extent1] 
WHERE ([Extent1].[ClassA_ID] IS NOT NULL) AND ([Extent1].[ClassA_ID] = @EntityKeyValue1) 

Me gustaría que consulta generada sería algo como esto:

SELECT [Data] AS [Data] 
FROM (SELECT 
     [Data] AS [Data], 
     rownum = ROW_NUMBER() OVER (ORDER BY [B].[ID]) 
     FROM ClassBs AS B , ClassAs AS A 
     WHERE B.ClassA_ID = A.ID 
     AND A.ID = 2) AS T1 
WHERE [t1].rownum BETWEEN 11 AND 20 
ORDER BY [t1].rownum 

El gran problema es que la clase A -> Clase B siempre tener más de 10k líneas, y el De esta forma, todas estas líneas se están cargando en la memoria, y la búsqueda se está realizando en la memoria, pero me gustaría que el servidor SQL lo hiciera.

¿Alguna idea sobre cómo lograr esto?

Respuesta

2

Debe diferenciar entre linq-to-entities y linq-to-objects. Esto:

var classA = (from x in db.ClassAs 
       where x.ID == 2 
       select x).First(); 

es linq-to-entities. Está accediendo al db.ClassAs proporcionando IQueryable para construir el árbol de expresiones que se ejecutará como SQL en la base de datos cuando llame al First(). Pero esto:

var classesB = (from b in classA.Children 
       select b.Data).Skip(10).Take(10); 

es linq-to-objects. La consulta en sí se define en la colección (HashSet) y se ejecuta en esa colección. Debido a que su propiedad está marcada como virtual, EF activará la carga lenta y completará todos los datos en esa propiedad para permitir que se ejecute en la consulta de la memoria. Nunca funcionará de otra manera.

Si desea realizar la consulta de base de datos para su propiedad relacionada debe utilizar la carga explícita en lugar de carga diferida:

db.Entry(classA) 
    .Collection(c => c.Children) 
    .Query() 
    .OrderBy(...) // You must order entities before you can use Skip and Take 
    .Skip(10) 
    .Take(10) 
    .Load(); 

var classesB = classA.Children; 
+0

Gracias! ¡Funcionó! –

Cuestiones relacionadas