15

我无法检测导航属性的更改:

我的测试模型如下所示:

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 类型的对象,同时分配了 Name 和 Address 属性。我的问题是,如果我从数据库中取回 Person 对象并更改 Address 属性(例如更改为 Null),则 ef 不会检测到更改!我的代码是这样的:

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(x => x.Address).First(); 加载 Person 对象 解决问题。有没有办法避免调用 Include 并继续修改 Address 属性而不是 AddressId ?

4

3 回答 3

27

首先:您必须按照@billy 的建议使用Include. 您的评论“ p.Address IS NOT NULL! ”仅是正确的,因为您正在p.Address调试器中观看,从而触发调试器中的延迟加载,因此null检测到设置地址的更改。在发布模式下或当您不检查调试器中的属性时,您的代码将无法工作,并且不会保存任何更改。

因此,您的编辑 3 的答案是:不。

第二:var entry = ctx.Entry(p)只返回实体状态,并且您没有更改实体状态,而是更改了关系状态,或者更准确地说,您删除了关系。您不能使用 API 检查关系状态,DbContext而只能使用ObjectContextAPI:

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

在此处输入图像描述

SaveChanges()当您调用和删除关系时, EF 会将此关系条目与实体状态条目一起考虑,即将数据库中的Address外键列设置为。PersonNULL

关于编辑 2:更改外键属性(这是模型中的标量属性)是实体本身的更改,因此Modified在这种情况下实体状态将是。

于 2012-07-26T14:36:18.013 回答
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);
}
于 2012-07-26T13:33:07.147 回答
2

在我的应用程序中,在请求重新加载或用户离开项目/视图之前,我会执行一些检查以确保没有未保存的更改。

这基本上超出了当前接受的答案,但我想提供一个实现并提请注意您必须在可以获取关系更改Context.ChangeTracker.DetectChanges()之前调用!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...
于 2013-08-19T00:33:00.940 回答