实体框架不会检测导航属性的更改

Mon*_*nes 15 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 Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我创建并保存了一个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);
}
Run Code Online (Sandbox Code Playgroud)

为什么entry.State 不变

编辑:如果我调用SaveChanges,记录将正确保存(地址变为空)!

编辑2:我已经创建了billy建议的外键属性,如果我在visual studio中检查Person对象,则State被修改..如果我不停止调试器检查对象的值,状态是不变的!

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

Sla*_*uma 26

首先:你必须遵循@ billy的建议才能使用Include.您的注释" p.Address IS NOT NULL! "仅为真,因为您正在p.Address调试器中观察并因此在调试器中触发延迟加载,因此null检测到设置地址的更改.在发布模式下或当您不检查调试器中的属性时,您的代码将无法工作,并且不会保存任何更改.

那么,你的编辑3的答案是:不.

第二:var entry = ctx.Entry(p)仅返回实体状态而您没有更改实体状态,而是更改关系状态,或者更确切地说,您删除了关系.您无法使用DbContextAPI 检查关系状态,只能使用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);
Run Code Online (Sandbox Code Playgroud)

objentr将有一个类型的条目RelationshipEntry现在:

在此输入图像描述

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

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


bil*_*lly 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);
}
Run Code Online (Sandbox Code Playgroud)

您也可以在模型中使用外键,我非常喜欢:

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

    public string Name { get; set; }

    public virtual Address Address { get; set; }

    public int? AddressId {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

...

using (var ctx = new EFContext())
{
    Person p = ctx.People.First();
    p.AddressId = null;
    var entry = ctx.Entry(p);
}
Run Code Online (Sandbox Code Playgroud)