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

时间:2012-07-26 13:19:14

标签: 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; }
}

我已创建并保存了Person类型的对象,并指定了Name和Address属性。我的问题是,如果我从数据库中取回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对象,则State被修改..如果我不停止调试器检查对象的值状态是不变的!

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

3 个答案:

答案 0 :(得分:26)

首先:您必须遵循@ 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

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

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

答案 1 :(得分: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);
}

答案 2 :(得分: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...