関連データの一括読み込み
一括読み込み
Include
メソッドを使用して、クエリ結果に含める関連データを指定することができます。 次の例では、結果で返されるブログには Posts
プロパティに関連する投稿が設定されます。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ToList();
}
ヒント
Entity Framework Core は、以前にコンテキスト インスタンスに読み込まれた他のエンティティに対して、ナビゲーション プロパティを自動的に修正します。 そのため、ナビゲーション プロパティのデータを明示的に含めていない場合でも、関連エンティティの一部またはすべてが以前に読み込まれていれば、プロパティを設定することができます。
複数のリレーションシップの関連データを 1 つのクエリに含めることができます。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.Include(blog => blog.Owner)
.ToList();
}
注意事項
コレクション ナビゲーションを 1 つのクエリで一括で読み込むと、パフォーマンスの問題が発生する可能性があります。 詳細については、単一クエリと分割クエリに関するページをご覧ください。
複数のレベルを含める
ThenInclude
メソッドを使用して、リレーションシップをドリル ダウンし、複数のレベルの関連データを含めることができます。 次の例では、すべてのブログ、関連記事、および各投稿の作成者を読み込みます。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList();
}
ThenInclude
に対して複数の呼び出しを連鎖させて、さらなるレベルの関連データを含めることができます。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.ToList();
}
これらのすべての呼び出しを組み合わせて、複数のレベルと複数のルートの関連データを同じクエリ内に含めることができます。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.Include(blog => blog.Owner)
.ThenInclude(owner => owner.Photo)
.ToList();
}
含まれているエンティティの 1 つについて複数の関連エンティティを含めることができます。 たとえば、Blogs
をクエリするときに、Posts
を含め、さらに Posts
の Author
と Tags
の両方を含めたい場合があります。 両方を含めるには、ルートから始まる各インクルード パスを指定する必要があります。 たとえば、Blog -> Posts -> Author
とBlog -> Posts -> Tags
です。 これで冗長的な結合を実現することにはならず、ほとんどの場合、SQL を生成するときに EF で結合は組み合わされます。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.Include(blog => blog.Posts)
.ThenInclude(post => post.Tags)
.ToList();
}
ヒント
1 つの Include
メソッドを使用して複数のナビゲーションを読み込むこともできます。 これは、すべて参照であるナビゲーション "チェーン" の場合、または 1 つのコレクションで終わる場合に可能です。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Owner.AuthoredPosts)
.ThenInclude(post => post.Blog.Owner.Photo)
.ToList();
}
フィルター処理されたインクルード
インクルードを適用して関連データを読み込む場合は、インクルードされているコレクション ナビゲーションに特定の列挙可能な操作を追加できます。これにより、結果のフィルター処理と並べ替えを行うことができます。
サポートされている操作は、Where
、OrderBy
、OrderByDescending
、ThenBy
、ThenByDescending
、Skip
、Take
です。
このような操作は、次の例に示すように、インクルード メソッドに渡されるラムダのコレクション ナビゲーションに適用する必要があります。
using (var context = new BloggingContext())
{
var filteredBlogs = context.Blogs
.Include(
blog => blog.Posts
.Where(post => post.BlogId == 1)
.OrderByDescending(post => post.Title)
.Take(5))
.ToList();
}
インクルードされている各ナビゲーションでは、フィルター操作の一意のセットが 1 つだけ許可されます。 特定のコレクション ナビゲーション (次の例では blog.Posts
) に複数のインクルード操作が適用されている場合、フィルター操作はそのいずれかでのみ指定できます。
using (var context = new BloggingContext())
{
var filteredBlogs = context.Blogs
.Include(blog => blog.Posts.Where(post => post.BlogId == 1))
.ThenInclude(post => post.Author)
.Include(blog => blog.Posts)
.ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
.ToList();
}
代わりに、複数回インクルードされているナビゲーションごとに、同一の操作を適用することもできます。
using (var context = new BloggingContext())
{
var filteredBlogs = context.Blogs
.Include(blog => blog.Posts.Where(post => post.BlogId == 1))
.ThenInclude(post => post.Author)
.Include(blog => blog.Posts.Where(post => post.BlogId == 1))
.ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
.ToList();
}
注意事項
クエリの追跡では、ナビゲーション修正により、フィルター処理されたインクルードの結果が予期しないものになることがあります。 以前にクエリされ、変更トラッカーに格納されていた関連エンティティはすべて、フィルターの要件を満たしていなくても、フィルター処理されたインクルードのクエリの結果に表示されます。 そのような状況でフィルター処理されたインクルードを使用する場合は、NoTracking
クエリを使用するか、DbContext を再作成することを検討してください。
例:
var orders = context.Orders.Where(o => o.Id > 1000).ToList();
// customer entities will have references to all orders where Id > 1000, rather than > 5000
var filtered = context.Customers.Include(c => c.Orders.Where(o => o.Id > 5000)).ToList();
Note
追跡クエリの場合は、フィルター処理されたインクルードが適用されたナビゲーションが読み込まれると見なされます。 つまり、EF Core では、一部の要素が欠落してる可能性があっても、明示的読み込みまたは遅延読み込みを使用した値の再読み込みが試みられません。
派生型に対するインクルード
Include
と ThenInclude
を使用して、派生型にのみ定義されているナビゲーションの関連データを含めることができます。
次のモデルがあるとします。
public class SchoolContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<School> Schools { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<School>().HasMany(s => s.Students).WithOne(s => s.School);
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Student : Person
{
public School School { get; set; }
}
public class School
{
public int Id { get; set; }
public string Name { get; set; }
public List<Student> Students { get; set; }
}
Student であるすべての People の School
ナビゲーションの内容を一括して読み込むには、多くのパターンを使用できます。
キャストを使用する
context.People.Include(person => ((Student)person).School).ToList()
as
演算子を使用するcontext.People.Include(person => (person as Student).School).ToList()
型
string
のパラメーターを受け取るInclude
のオーバーロードを使用するcontext.People.Include("School").ToList()
自動インクルード ナビゲーションのモデル構成
AutoInclude
メソッドを使って、データベースからエンティティが読み込まれるたびに、モデルにナビゲーションが含まれるように構成することができます。 これは、エンティティ型が結果で返されるすべてのクエリにおいて、ナビゲーションで Include
を指定するのと同じ効果があります。 次の例では、ナビゲーションを自動的に含めるように構成する方法を示します。
modelBuilder.Entity<Theme>().Navigation(e => e.ColorScheme).AutoInclude();
上記の構成の後、次のようなクエリを実行すると、結果にすべてのテーマの ColorScheme
ナビゲーションが読み込まれます。
using (var context = new BloggingContext())
{
var themes = context.Themes.ToList();
}
この構成は、結果にどのように表示するかに関係なく、結果で返されたすべてのエンティティに適用されます。 つまり、ナビゲーションを使用したり、別のエンティティ型または自動インクルード構成に Include
を使用したりすることによってエンティティが結果に含まれている場合は、その自動インクルード ナビゲーションがすべて読み込まれます。 このルールは、エンティティの派生型で自動インクルードとして構成されたナビゲーションにも及びます。
特定のクエリにおいて、モデル レベルで自動インクルードされるように構成されているナビゲーションを使用して関連データを読み込まない場合は、クエリで IgnoreAutoIncludes
メソッドを使用できます。 このメソッドを使用すると、ユーザーによって自動インクルードとして構成されたナビゲーションの読み込みがすべて停止されます。 次のようなクエリを実行すると、データベースからすべてのテーマが返されますが、自動インクルード ナビゲーションとして構成されている場合でも ColorScheme
の読み込みは行われません。
using (var context = new BloggingContext())
{
var themes = context.Themes.IgnoreAutoIncludes().ToList();
}
Note
所有型へのナビゲーションも規則によって自動インクルードとして構成されており、IgnoreAutoIncludes
API を使用しても、それらが含まれなくなることはありません。 クエリ結果には引き続き含まれます。