Optimiser le SQL généré par LINQ Query dans Entity Framework 4.1 avec des associations un-à-plusieurs

J'ai quelques problèmes avec la requête Sql générée par LINQ, Puisque mon environnement est assez grand, j'ai fait un exemple simple qui reflète mon problème.

C'est mon model:

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

Très simple hein?

Eh bien, voici ma requête:

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

Le problème est lorsque cette requête est traduite en SQL:

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

devient:

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

et:

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

devient:

 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) 

Je souhaite que la requête générée soit quelque chose comme ceci:

 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 

Le gros problème est que la class A -> class B a toujours plus de 10k lignes, et comme c'est le cas, toutes ces lignes sont chargées en memory, et la pagination est en memory, mais je souhaite que cette pagination être fait par le server SQL.

Des pensées sur la façon d'accomplir cela?

Vous devez différer entre linq-to-entities et linq-to-objects. Ce:

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

est linq-to-entities. Vous db.ClassAs à db.ClassAs fournissant IQueryable pour générer l'tree d'expression qui sera exécuté en tant que SQL dans la database lorsque vous appelez First() . Mais ça:

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

est linq-to-objects. La requête elle-même est définie sur la collection ( HashSet ) et exécutée sur cette collection. Parce que votre propriété est marquée comme virtual EF triggersra le chargement différé pour vous et remplira toutes datatables dans cette propriété pour vous permettre d'exécuter cela dans la requête de memory. Cela ne marchera jamais de manière différente.

Si vous voulez faire une requête de database pour votre propriété liée, vous devez utiliser le chargement explicite au lieu du chargement paresseux:

 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 classsB = classA.Children;