エンティティフレームワークlinqクエリInclude()複数の子エンティティ


176

これは本当に基本的な質問かもしれませんが、3レベル(またはそれ以上)にまたがるクエリを作成するときに複数の子エンティティを含めるための良い方法は何ですか?

つまり、私は4つのテーブルを持っている:CompanyEmployeeEmployee_CarおよびEmployee_Country

会社は従業員と1:mの関係があります。

従業員は、Employee_CarとEmployee_Countryの両方と1:mの関係にあります。

4つのテーブルすべてからデータを返すクエリを記述したい場合は、現在次のように記述しています。

Company company = context.Companies
                         .Include("Employee.Employee_Car")
                         .Include("Employee.Employee_Country")
                         .FirstOrDefault(c => c.Id == companyID);

よりエレガントな方法が必要です!これは長く巻き込まれ、恐ろしいSQLを生成します

VS 2010でEF4を使用しています

回答:


201

拡張メソッドを使用しますNameOfContextをオブジェクトコンテキストの名前に置き換えます。

public static class Extensions{
   public static IQueryable<Company> CompleteCompanies(this NameOfContext context){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country") ;
     }

     public static Company CompanyById(this NameOfContext context, int companyID){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country")
             .FirstOrDefault(c => c.Id == companyID) ;
      }

}

次に、コードは

     Company company = 
          context.CompleteCompanies().FirstOrDefault(c => c.Id == companyID);

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);

:しかし、私は同じように、これを使用したいと思います //inside public static class Extensions public static IQueryable<Company> CompleteCompanies(this DbSet<Company> table){ return table .Include("Employee.Employee_Car") .Include("Employee.Employee_Country") ; } //code will be... Company company = context.Companies.CompleteCompanies().FirstOrDefault(c => c.Id == companyID); //same for next advanced method
ハミド

Bullsye Nix。拡張機能は、事前定義された機能を拡張するための最初の呼び出しポートである必要があります。
ComeIn

12
数年後、文字列ベースのインクルードはランタイムセーフではないため、お勧めしません。ナビゲーションプロパティの名前が変更されたり、スペルに誤りがあると、破損します。代わりに型付きインクルードを使用することを強くお勧めします。
Jeff Putz 2016年

2
nameof(class)の導入以来、このアプローチを安全に使用することが可能です。エンティティ名が変更された場合、コンパイル時に選択されます。例:context.Companies.Include(nameof(Employee))さらに下に移動する必要がある場合は、nameof(Employee)+ "。" + nameof(Employee_Car)と連結する必要があります
Karl

拡張メソッド手法は、ここで確認されたコンパイル済みクエリ(少なくともEFCoreでは無効)では機能しません:github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge

156

EF 4.1からEF 6

あり、強く型付けされた.Include積極的なロードの必要な深さが適切な深さに選択式を提供することで指定することができるようになります:

using System.Data.Entity; // NB!

var company = context.Companies
                     .Include(co => co.Employees.Select(emp => emp.Employee_Car))
                     .Include(co => co.Employees.Select(emp => emp.Employee_Country))
                     .FirstOrDefault(co => co.companyID == companyID);

両方のインスタンスで生成されたSQLは、まだ直感的ではありませんが、十分に機能しているようです。ここGitHubに小さな例を載せまし

EFコア

EF Coreには新しい拡張メソッド.ThenInclude()がありますが、構文は少し異なります。

var company = context.Companies
                     .Include(co => co.Employees)
                           .ThenInclude(emp => emp.Employee_Car)
                      ...

ドキュメントに従って、私は.ThenIncludeあなたの正気を保つために余分な「インデント」を保持します。

廃止された情報(これを行わないでください):

複数の孫の読み込み 1つのステップで行うことできますが、次のノードに進む前に、グラフを逆向きに戻す必要があります(注:これは機能しませんAsNoTracking()-ランタイムエラーが発生します)。

var company = context.Companies
         .Include(co => 
             co.Employees
                .Select(emp => emp.Employee_Car
                    .Select(ec => ec.Employee)
                    .Select(emp2 => emp2.Employee_Country)))
         .FirstOrDefault(co => co.companyID == companyID);

したがって、最初のオプション(リーフエンティティの深さモデルごとに1つ含める)のままにします。


4
強く型付けされた.Includeステートメントを使用してそれを行う方法を考えていました。Selectで子供を投影することが答えでした!

1
私の「co.Employees.Select(...)」は、「「従業員」には「選択」[または拡張メソッド]の定義が含まれていない」という「選択」の構文エラーを示しています。System.Data.Entityを含めました。結合されたテーブルから単一の列を取得したいだけです。
クリスウォルシュ

1
同じ子テーブルを2回参照する親テーブルがありました。古い文字列インクルード構文では、正しい関係をプリロードすることが困難でした。この方法はより具体的です。厳密に型指定されたインクルードの名前空間System.Data.Entityを含めることを覚えておいてください。
Karl

1
.netコア2.1では、System.Data.Entityではなく名前空間Microsoft.EntityFrameworkCoreが必要でした
denvercoder9

27

この興味深い記事はcodeplex.comから入手できます。

この記事では、複数のテーブルにまたがるクエリを宣言的なグラフの形で表現する新しい方法を紹介します。

さらに、この記事には、この新しいアプローチとEFクエリの完全なパフォーマンス比較が含まれています。この分析は、GBQがEFクエリよりもすぐに優れていることを示しています。


これを実際のアプリケーションにどのように実装できますか?
Victor.Uduak

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.