Entity Frameworkの関連のロード


以下のEntityでFoo.Barsのロードの説明。

public class SampleContext: DbContext
{
    public DbSet<Foo> Foos { get; set; }
    public DbSet<Bar> Bars { get; set; }
}

public class Foo
{
    public long Id { get; set; }
    public virtual ICollection<Bar> Bars { get; set; }
}

public class Bar
{
    public long Id { get; set; }
    public long FooId { get; set; }
    [ForeignKey("FooId")]
    public virtual Foo Foo { get; set; }
}

LazyLoading

LazyLoadingEnabledがtrue

はじめてのfoo.Barsアクセス時にLazyロードが走ります。
LazyLoadingEnabledはtrueがデフォルト。

var foo = default(Foo);
using (var context = new SampleContext())
{
    foo = context.Foos.Where(f => f.Id == 1).Single();
    var bars1 = foo.Bars; // ここでLazyロード
}
var bar2 = foo.Bars; // いったんロードされているのでちゃんと取れる
var foo = default(Foo);
using (var context = new SampleContext())
{
    foo = context.Foos.Where(f => f.Id == 1).Single();
}
// contextは破棄されているのにここでLazyロードしようとしてObjectDisposedException
var bars = foo.Bars;

LazyLoadingEnabledがfalse

var foo = default(Foo);
using (var context = new SampleContext())
{
    context.Configuration.LazyLoadingEnabled = fales;
    foo = context.Foos.Where(f => f.Id == 1).Single();
    var bars = foo.Bars; // Lazyロードされずにbarsはnull
}
var foo = default(Foo);
using (var context = new SampleContext())
{
    context.Configuration.LazyLoadingEnabled = fales;
    foo = context.Foos.Where(f => f.Id == 1).Single();
}
var bars = foo.Bars; // Lazyロードされずにbarsはnull

明示的にロード

明示的にロードしておけばcontext.Configuration.LazyLoadingEnabledにかかわらずcontext内外でBarsを読めます。

Include()

Include()を使うとFooとBarをまとめてロードできます。
基本的には処理に必要な関連はInclude()で一気にロードしておくべきです。
Include()はcontext.Foos.Include(f => f.Bars).Include(f => f.AnotherAssoc)のようにつなげて書けます。

var foo = default(Foo);
using (var context = new SampleContext())
{
    // ここでロード
    foo = context.Foos.Include(f => f.Bars).Where(f => f.Id == 1).Single();

    var bars1 = foo.Bars; // 普通に取れる
}
var bars2 = foo.Bars; // 普通に取れる

Load()

Load()を使うとBarを個別にロードできます。
Fooのロード量が多く、かつ、Barsをロードする必要があるFooは少量なときは、
Include()よりLoad()またはLazyロードに任せるのがいいです。

var foo = default(Foo);
using (var context = new SampleContext())
{
    foo = context.Foos.Where(f => f.Id == 1).Single();

    context.Entry(foo).Collection(f => f.Bars).Load(); // ここでロード

    var bars1 = foo.Bars; // 普通に取れる
}
var bars2 = foo.Bars; // 普通に取れる