2016-09-20 10 views
1

背景:EF6とデータベースファーストを使用しています。EF6でdbContext.Saveを呼び出した後、ナビゲーションプロパティがリセットされる理由

私は困惑しているシナリオに遭遇しています。新しいオブジェクトを作成し、新しいオブジェクトでナビゲーションプロパティを設定し、SaveChangesを呼び出すと、ナビゲーションプロパティがリセットされます。 SaveChanges呼び出し後にナビゲーションプロパティを参照するコードの最初の行は、データベースからデータを再フェッチすることになります。これは期待された動作ですか、誰かがこのように動作する理由を説明できますか?私のシナリオのサンプルコードブ​​ロックは次のとおりです。

using (DbContext context = new DbContext) { 
    Foo foo = context.Foos.Create(); 
    context.Foos.Add(foo); 
    ... 
    Bar bar = context.Bars.Create(); 
    context.Bars.Add(bar); 
    ... 
    FooBar foobar = context.FooBars.Create(); 
    context.FooBars.Add(foobar) 
    foobar.Foo = foo; 
    foobar.Bar = bar; 

    //foo.FooBars is already populated, so 1 is returned and no database query is executed. 
    int count = foo.FooBars.Count; 

    context.SaveChanges(); 

    //This causes a new query against the database - Why? 
    count = foo.FooBars.Count; 
} 

答えて

1

私は100%とは言えませんが、この動作が具体的に行われたかどうかは疑問です。それはこのように動作する理由については

、問題の根は、ナビゲーションプロパティはLoadメソッドを使用してロードされ、暗黙的に遅延ロードまたは明示的にどちらかになるまでDbCollectionEntry.IsLoadedプロパティがfalseであるということです。

また、エンティティが追加状態になっている間に遅延読み込みが抑制されているようです。そのため、最初の呼び出しでリロードがトリガーされません。しかし、SaveChangesを呼び出すと、エンティティの状態は変更されなくなり、コレクションが実際にはヌルに設定されていないかクリアされていなくても、にアクセスしてにアクセスしようとすると、遅延再ロードが発生します。

// ... 

Console.WriteLine(context.Entry(foo).Collection(e => e.FooBars).IsLoaded); // false 
//foo.FooBars is already populated, so 1 is returned and no database query is executed. 
int count = foo.FooBars.Count; 

// Cache the collection property into a variable 
var foo_FooBars = foo.FooBars; 

context.SaveChanges(); 

Console.WriteLine(context.Entry(foo).State); // Unchanged! 
Console.WriteLine(context.Entry(foo).Collection(e => e.FooBars).IsLoaded); // false 

// The collection is still there, this does not trigger database query 
count = foo_FooBars.Count; 
// This causes a new query against the database 
count = foo.FooBars.Count; 

あなたはリロードを避けたい場合は、回避策は明示的trueIsLoadedプロパティを設定することです。

// ... 

context.SaveChanges(); 

context.Entry(foo).Collection(e => e.FooBars).IsLoaded = true; 
context.Entry(bar).Collection(e => e.FooBars).IsLoaded = true; 
// No new query against the database :) 
count = foo.FooBars.Count; 
+0

優秀な説明、ありがとうございます! – cas4

0

なぜデータベースを再クエリしないと思いますか? EFはデータベースの完全なキャッシュコピーを保持していません。変更したものの後に行挿入を行うトリガーがある場合、またはEFが正確な計算を知らない関数列として機能する列がある場合はどうなりますか?最新のデータを取得する必要があります。それ以外の場合は、Countは正しくありません。

0

私は問題が「コンテキスト」という概念を混乱させていると思います。あなたがEFの文脈にいるときは、あなたは「接続された」状態にあります。データをスマートに再利用することが常に気になるわけではありません。 「私は、話すために設定されたコンテキストを通じて、データベースレベルの拡張として存在する」と認識しています。あなたが作成したオブジェクトまたは1つを持っている場合は、既存の「FooBars」と言うと、あなたはこれを行う:

foo.Foobars.(some operation) 

fooはあなたのコンテキストで、Foobarsはそれをオフにいくつかのオブジェクトである場合、それは文脈の延長として参照されます。データベースへのラウンドトリップを招くことなく、再利用を実現する場合のようなコンテキストの範囲外のオブジェクトやオブジェクトの収集:

var foobars= new Foobars; 

using(var foo = new new DbContext) 
{ 
    foobars = foo.FooBars; 
} 

var cnt = foobars.Count(); 

は、一般的にEFと話を、私は「全体を持っているように多くの人が何かを見ますメソッド全体に(var context = new Efcontext()) 'を使用し、このプロセスをどこでも繰り返し実行できます。それは箱から外に悪いことではないが、どこでもこれをやって、私は単に言っているだろう: "あなたはDB接続を何度も何度も開いておく必要がありますか?"時には、あなたはそうしますが、ほとんどの場合、あなたはしません。

関連する問題