C# 实体框架赢得';无法检测导航属性的更改

C# 实体框架赢得';无法检测导航属性的更改,c#,entity-framework-4,C#,Entity Framework 4,我在检测导航属性的更改时遇到问题: 我的测试模型如下所示: 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 N

我在检测导航属性的更改时遇到问题:

我的测试模型如下所示:

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; }
}
我已经创建并保存了一个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:我已经按照billy的建议创建了外键属性,如果我在visual studio中检查Person对象,状态将被修改。。如果我不停止调试器检查对象的值,状态将保持不变


编辑3:使用ctx.People.Include(x=>x.Address.First()加载Person对象;解决了这个问题。有没有办法避免调用Include并继续修改Address属性而不是AddressId属性?

您需要包括Address nav。道具在您的查询中,否则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;}
}


首先:你必须按照@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
的条目:

EF在调用<代码> SaveCechange()>代码>时,将该关系条目与实体状态条目一起考虑,并删除关系,即将<<代码>地址>代码>数据库中的<代码>人>代码>的外键列设置为<代码> null < /代码> .<
关于编辑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...

如果调用ctx.DetectChanges(),会发生什么?没有。结果是一样的!!我已经用DetectChanges测试了解决方案,但没有办法。。仍然保持不变的状态!如果我使用int?外键我想它会正常工作,因为我要更改“简单”属性的值,而不是直接更改导航属性。事实上,如果我更改Person对象的Name属性,一切都可以!回答得不错,但我有一个疑问。我如何确定某个特定条目发生了此更改而不是其他更改,因为这是对特定上下文的查询。您如何在EF Core中执行此操作,因为没有
ObjectContext
?我已经使用类似的代码跟踪更改很长一段时间了。最近我开始给我的模型添加外键。但是当我删除一个实体时,
ObjectStateManager
的已删除关系列表中不会显示具有外键的关系。你知道为什么吗?
_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...