5

LINQで生成されたSqlクエリに問題があります 私の環境がかなり大きいので、私は問題。1対多の関係でEntity Framework 4.1でLINQクエリによって生成されたSQLを最適化

これは私のモデルである:

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; } 
} 

非常に簡単でしょ?

まあ、これは私のクエリです:

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(); 

このクエリはSQLに変換されますときに問題がある:

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

は次のようになります。

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

と:

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

次のようになります。

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) 

私は生成されたクエリは、このようなものになるだろうことを望む:大きな問題は、クラスAということである

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 

- >クラスBは常に10Kラインよりも、と持っていますつまり、これらの行はすべてメモリにロードされており、ページングは​​メモリ内で行われていますが、このページングは​​SQL Serverによって実行されることを望みます。

これを達成する方法についてのご意見はありますか?

答えて

2

linq-to-entitiesとlinq-to-objectsの間で異なる必要があります。これは:

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

はlinq-to-entitiesです。 First()を呼び出すと、db.ClassAsIQueryableという式ツリーを作成し、データベースでSQLとして実行されます。しかし、これは:

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

はlinq-to-objectsです。クエリ自体はコレクション(HashSet)で定義され、そのコレクションで実行されます。あなたのプロパティはvirtualとマークされているので、EFは遅延読み込みをトリガし、そのプロパティのすべてのデータを埋め込んで、メモリクエリで実行できるようにします。それは決して異なった方法で働くことはありません。

あなたは、関連するプロパティのデータベースクエリを作成したい場合は、遅延ロードするのではなく、明示的なロードを使用する必要があります。

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

感謝を!出来た! –

関連する問題