2012-07-26 29 views
14

我在與檢測導航屬性的變化麻煩導航屬性的變化:實體框架不會檢測

我的測試模型是這樣的:

public class Person 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public virtual Address Address { get; set; } 
} 

public class Address 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 
} 

我已經創建並保存具有Name和Address屬性的Person類型的對象已分配。我的問題是,如果我從數據庫中取回Person對象並更改Address屬性(例如Null),那麼e.f.沒有檢測到變化! 我的代碼是這樣的:

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.First(); 
    //p.Address IS NOT NULL! 
    p.Address = null; 
    var entry = ctx.Entry(p); 
} 

爲什麼entry.State不變

編輯:如果我調用SaveChanges,記錄被正確保存(地址變爲空)!

編輯2:我創建了外鍵屬性作爲比利建議,如果我檢查在Visual Studio中的Person對象的狀態被修改..如果我不與調試器停止檢查對象的值狀態不變!

編輯3:使用ctx.People.Include加載Person對象(x => x.Address).First();解決了這個問題。有沒有辦法避免調用Include並繼續修改Address屬性而不是AddressId?

+0

如果您調用ctx.DetectChanges(),會發生什麼? – Maarten 2012-07-26 13:30:41

+0

什麼都沒有。結果是一樣的!! – Mones 2012-07-26 13:31:22

回答

24

首先:您必須按照@ billy的建議使用Include。您的評論「p.Address IS NOT NULL!」只是因爲您在調試器中觀看p.Address,從而觸發調試器中的延遲加載,因此檢測到將地址設置爲null的更改。在發佈模式下或者當您不檢查調試器中的屬性時,您的代碼將無法工作,並且不會保存更改。

所以,回答你的編輯3:第

二:var entry = ctx.Entry(p)只返回實體狀態,你沒有改變實體狀態,而是一個關係狀態,或者更準確地說,你刪除了的關係。你不能檢查關係狀態用DbContext API,但只能用ObjectContext API:

Person p = ctx.People.Include(x => x.Address).First(); 
p.Address = null; 
var objCtx = ((IObjectContextAdapter)ctx).ObjectContext; 
var objentr = objCtx.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted); 

objentr將有RelationshipEntry類型的條目現在:

enter image description here

EF會考慮這個關係條目當您撥打SaveChanges()並刪除關係時,即將PersonAddress外鍵列設置爲NULL

關於編輯2:更改外鍵屬性(這是模型中的標量屬性)是實體本身的更改,因此在這種情況下實體狀態將爲Modified

+0

哇!非常感謝你的回答! – 2014-02-05 16:21:11

+0

很好的答案,但我有疑問。我如何確定某個特定條目遭受了此更改而不是其他條件,因爲這是對特定上下文的查詢。 – 2014-12-30 17:31:22

+0

你如何在EF Core中這樣做,因爲沒有ObjectContext? – grokky 2016-12-13 21:32:00

5

您需要包含地址導航。支柱。在您的查詢,否則EF不會當你保存考慮修改它:

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.Include(x => x.Address).First(); 
    //p.Address IS NOT NULL! 
    p.Address = null; 
    var entry = ctx.Entry(p); 
} 

你也可以在你的模型,這是我非常喜歡使用外鍵:

public class Person 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public virtual Address Address { get; set; } 

    public int? AddressId {get; set;} 
} 

...

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.First(); 
    p.AddressId = null; 
    var entry = ctx.Entry(p); 
} 
+0

我用DetectChanges測試瞭解決方案,但沒辦法..仍然沒有變化的狀態!如果我使用int?外鍵我想它會正常工作,因爲我要改變「簡單」屬性的值而不是直接導航屬性。事實上,如果我更改Person對象的Name屬性,那麼一切正常! – Mones 2012-07-26 13:49:51

2

在我的應用程序中,在請求重新加載或用戶離開項目/視圖之前,我會執行一些檢查以確保沒有未保存的更改。

這基本上運行了目前接受的答案,但我想提供一個實施並提請注意,您必須在ObjectContext.ObjectStateManager可以取得關係更改之前致電Context.ChangeTracker.DetectChanges()!我花了相當多的時間調試這個,很傻!

_EagleContext.ChangeTracker.DetectChanges(); 

var objectContext = ((IObjectContextAdapter)_EagleContext).ObjectContext; 
var changedEntities = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified); 

if (_EagleContext.ChangeTracker.Entries().Any(e => e.State == EntityState.Modified) 
    || changedEntities.Count() != 0) 
{ 
    var dialogResult = MessageBox.Show("There are changes to save, are you sure you want to reload?", "Warning", MessageBoxButton.YesNo); 
    if (dialogResult == MessageBoxResult.No) 
    { 
     return; 
    } 
} 

// Continue with reloading...