2012-07-26 114 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...