2016-09-20 25 views
1

背景:我正在使用EF6和Database First。爲什麼在調用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%,但我懷疑這種行爲是否具體。

至於爲什麼它的行爲這樣一來,問題的根源在於DbCollectionEntry.IsLoaded屬性爲false,直到導航屬性是隱式延遲加載或使用Load方法明確加載。

似乎延遲加載被抑制,而實體處於新增狀態,這就是爲什麼第一次調用不會觸發重新加載。但是,一旦你調用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; 

如果你想避免重裝,解決辦法是IsLoaded屬性明確設置爲true

// ... 

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環境中時,您處於「已連接」狀態。它並不總是在乎重複使用你的數據。它知道「我作爲數據庫級別的擴展存在,通過設置與它交談的上下文」。如果你有一個對象創建或一個已有的說「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連接嗎?」有時你會這麼做,大部分時間你都不會。

相關問題