web-dev-qa-db-ja.com

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

これは本当にelementryの質問かもしれませんが、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を使用しています

161
Nathan Liu

拡張メソッド を使用してください。 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);
187
Nix

EF 4.1からEF 6

厳密に型指定された.Include があります。これは適切な深さまでSelect式を提供することによって必要な積極的な読み込みの深さを指定することを可能にします。

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 here に載せました。

EFコア

EF Coreには新しい拡張方法、 .ThenInclude() がありますが、構文は わずかに異なります

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

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

古い情報(しないでください):

複数の孫の読み込みは1つのステップで実行できますが、これは次のノードに進む前にグラフを元に戻すためのやや厄介な反転を必要とします(NB:これは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つ含む)を使い続けるでしょう。

139
StuartLC

この記事は codeplex.com にあります。

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

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

27
Merijn

ReferenceプロパティやLoad()を呼び出すのではなく、子オブジェクトを直接読み込むLINQ to Entitiesクエリをどのように構築しますか

遅延ロードを実装する以外に他の方法はありません。

または手動ロード....

myobj = context.MyObjects.First();
myobj.ChildA.Load();
myobj.ChildB.Load();
...
4
Andreas Rehm