0

エンティティ フレームワークからデータを引き出すときに、Includes に問題があります。昨日、非常によく似た質問をここに投稿しました。これには、使用しようとしているのと同じサンプルがあります。Entity Framework v4.0 を使用しています。

次の単純なモデルがあります。これは、ページフォームのリスト (〜200) を含むテーブルです。各フォームには 1 つ以上のフィールド (合計で最大 4000) があり、各フィールドにはいくつかのパラメーター (合計で最大 16000) がある場合があります。

エンティティ モデル

次のコードを使用してデータを引き出します。

EntityConnection myConnection = new EntityConnection("name=myModel");

if(conn.State != ConnectionState.Open) {
    conn.Open();
}
ObjectContext context = new ObjectContext("name=myModel");
context.ContextOptions.LazyLoadingEnabled = false;

ObjectQuery<PageForm> myObjectSet = context.CreateObjectSet<PageForm>()
                                           .Include("FormFields.FormFieldParameters");

IQueryable<PageForm> myFilteredObjectSet = myObjectSet.Where(c => c.FormID == 1);

List<PageForm> myReturnValue = myFilteredObjectSet.toList();

次のSQLクエリを生成します

SELECT 
[Project1].[FormID] AS [FormID], 
[Project1].[FormName] AS [FormName], 
[Project1].[C2] AS [C1], 
[Project1].[FormID1] AS [FormID1], 
[Project1].[FieldID] AS [FieldID], 
[Project1].[FieldName] AS [FieldName], 
[Project1].[C1] AS [C2], 
[Project1].[FieldParamID] AS [FieldParamID], 
[Project1].[Value] AS [Value], 
[Project1].[FieldID1] AS [FieldID1]
FROM ( 
    SELECT 
    [Extent1].[FormID] AS [FormID], 
    [Extent1].[FormName] AS [FormName], 
    [Join1].[FieldID] AS [FieldID], 
    [Join1].[FieldName] AS [FieldName], 
    [Join1].[FormID] AS [FormID1], 
    [Join1].[FieldParamID] AS [FieldParamID], 
    [Join1].[Value] AS [Value], 
    [Join1].[FieldID1] AS [FieldID1], 
    CASE WHEN ([Join1].[FieldID] IS NULL) THEN CAST(NULL AS int) WHEN ([Join1].[FieldParamID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
    CASE WHEN ([Join1].[FieldID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
    FROM  [dbo].[PageForm] AS [Extent1]
    LEFT OUTER JOIN (
        SELECT 
        [Extent2].[FieldID] AS [FieldID], 
        [Extent2].[FieldName] AS [FieldName], 
        [Extent2].[FormID] AS [FormID], 
        [Extent3].[FieldParamID] AS [FieldParamID], 
        [Extent3].[Value] AS [Value], 
        [Extent3].[FieldID] AS [FieldID1]
        FROM  [dbo].[FormField] AS [Extent2]
        LEFT OUTER JOIN [dbo].[FormFieldParameter] AS [Extent3] ON [Extent2].[FieldID] = [Extent3].[FieldID] 
    ) AS [Join1] ON [Extent1].[FormID] = [Join1].[FormID]
    WHERE 1 = [Extent1].[FormID]
) AS [Project1]
ORDER BY [Project1].[FormID] ASC, [Project1].[C2] ASC, [Project1].[FieldID] ASC, [Project1].[C1] ASC

今、私はクエリのこの部分に興味があります:

LEFT OUTER JOIN (
    SELECT 
    /**/
    FROM  [dbo].[FormField] AS [Extent2]
    LEFT OUTER JOIN [dbo].[FormFieldParameter] AS [Extent3] ON [Extent2].[FieldID] = [Extent3].[FieldID] 
) AS [Join1] ON [Extent1].[FormID] = [Join1].[FormID]

これは、フィルタリングが適用されていない FormField および FormFieldParameter テーブル全体をクエリしているようです。したがって、私が考えていることは、使用.Include("FormField.FormFieldParameter")は、「結果のクエリでこれらのテーブルからすべてのデータを返す」ということと似ているということです。私が本当に欲しいのは、「フィルタリングされた PageForm テーブルに関連するこれらのテーブルからのデータのみを返す」ことです。

これを行う方法はありますか?この質問が単純すぎるか、前の質問と似ているように聞こえる場合は申し訳ありませんが、Entity Framework の内部について理解するのに本当に苦労しています。

編集1:

上記のサンプルを where 句を含む次のように変更すると、クエリは桁違いに高速になります (約 10 倍高速化)。

LEFT OUTER JOIN (
    SELECT 
    /**/
    FROM  [dbo].[FormField] AS [Extent2]
    LEFT OUTER JOIN [dbo].[FormFieldParameter] AS [Extent3] ON [Extent2].[FieldID] = [Extent3].[FieldID] 
    WHERE 1 = [Extent2].[FormID]
) AS [Join1] ON [Extent1].[FormID] = [Join1].[FormID]

編集2:

もう少し情報。最初のクエリにファンキーなフィルタリングを追加すると、より効率的なクエリを強制的に実行できることがわかりました。

IQueryable<PageForm> myFilteredObjectSet = myObjectSet
                                          .Where(c => c.FormID == 1)
                                          .Where(a => a.FormFields
                                                       .Where(c => c.FormFieldParameters
                                                                    .Any(d => d.FieldID == c.FieldID))
                                                       .Any(b => b.FormID == 1)
                                           );

これにより、クエリが正しくフィルター処理され、実行速度が大幅に向上します。ただし、これが最善の方法ではないことは確かです。ネストされた Where/Any ステートメントは、一握り以上のインクルードがある場合に対処するのがすぐに悪夢になるからです。もっと良い方法があるはずですか?

4

1 に答える 1

0

フィルタリングが適用されます - クエリには が含まれますWHERE 1 = [Extent1].[FormID]。リクエストした日付のみを実際にロードします。データベースのクエリ エンジンはクエリを正しく評価し、いくつかの最適化手法を使用してレコードを効率的にフィルター処理します。

于 2012-07-31T12:47:10.700 に答える