【问题标题】:Entity Framework Entry Change Tracking Issues实体框架条目更改跟踪问题
【发布时间】:2012-10-26 17:01:05
【问题描述】:

我有下面列出的 Profile 模型。此模型通过 ajax 调用发送到客户端,然后通过另一个 ajax 调用更新并发送回服务器。

当客户端进行更改时,他们负责设置他们正在使用的模型的 ObjectState。例如,如果他们正在更改 Name 属性,他们会将 Name 属性设置为新值,并将 ObjectState 属性设置为 2(已修改),并向地址添加新地址,他们会将新地址的 ObjectState 设置为 1 (添加)。这一切都很好,ApiController 正确接收新的 Profile 对象。

public enum ObjectState
{
    Unchanged = 0,
    Added = 1,
    Modified = 2,
    Deleted = 3
}

public interface IObjectState
{
    ObjectState ObjectState { get; set; }
}

static class EntityStateHelper
{
    public static EntityState ConvertState(ObjectState objectState)
    {
        switch (objectState)
        {
            case ObjectState.Added:
                return EntityState.Added;
            case ObjectState.Deleted:
                return EntityState.Deleted;
            case ObjectState.Modified:
                return EntityState.Modified;
            default:
                return EntityState.Unchanged;
        }
    }
}

// Models
public class Profile : IObjectState
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime? BirthDay { get; set; }

    // Navigation Properties
    public ICollection<Address> Addresses { get; set; }

    public ObjectState ObjectState { get; set; }
}

public class Address : IObjectState
{
    public int Id { get; set; }
    public string Street1 { get; set; }
    public string Street2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    public ObjectState ObjectState { get; set; }
}

在我的 ProfileRepository 内部,我实际上已经分离了 Insert 和 Update,在 Profile 对象的情况下,客户端没有删除配置文件的服务调用,所以他们对配置文件所能做的实际上就是修改它。在我的更新方法中,我选择使用_context.Entry&lt;T&gt;(entity)_context.Entry&lt;T&gt;(entity).State = EntityState.Modified 相对,因为这会引发异常,说明ObjectStateManager 中已经存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象

public void Update(T entity)
{
    _context.Entry<T>(entity);
    _context.ApplyStateChanges();
}

首先让我说,我在 Update 方法中实际上没有 ApplyStateChanges,但对于这个示例,它将显示正在发生的事情。以下是 ApplyStateChanges 方法的代码。

public static class DbContextEntensions
{
    public static void ApplyStateChanges(this DbContext context)
    {
        foreach (var trackableEntry in context.ChangeTracker.Entries<IObjectState>())
        {
            IObjectState state = trackableEntry.Entity;
            trackableEntry.State = EntityStateHelper.ConvertState(state.ObjectState);
        }
    }
} 

这就是问题所在。假设客户端的 Profile 是 Name 属性是 John,Addresses 属性包含 1 个地址。如果客户端将 Name 更改为 Steve 并修改 1 Address,那么当 ApplyStateChanges 被调用时,每个 DbEntityEntries 都会被标记为正确的 EntityState。当客户端将新地址添加到状态为 1(已添加)的地址集合时,就会出现问题。当 ApplyStateChanges 被调用 context.ChangeTracker.Entries&lt;IObjectState&gt;() 时,它不会将新添加的地址作为已被 ChangeTracker 更改的条目返回。

我很确定代码 _context.Entry&lt;T&gt;(entity) 应该是 _context.Entry&lt;T&gt;(entity).State = EntityState.Modified 但无论我尝试了什么,我似乎都无法绕过这个异常。

我将 EntityFramework 5.0 与 .NET 4.0 一起使用,这意味着 EntityFramework 版本是 4.4。我错过了什么?我已经阅读了我能找到的关于这个主题的每一篇文章,这看起来应该有效。我从 Julie Lerman 的 PluralSight 视频 Entity Framework in the Enterprise 中获得了这个状态变化跟踪代码。

任何帮助我指出正确的方向将不胜感激。

【问题讨论】:

    标签: entity-framework


    【解决方案1】:

    我建议在 Update 方法中使用 .Add(entity) 添加实体,这会将新地址标记为已添加并修复其导航属性。然后,当您调用 ApplyStateChanges 时,它会更正状态。

    【讨论】:

      【解决方案2】:

      这篇文章很旧,但它可能对其他人有所帮助:

      问题出在更新方法上。我假设您在断开连接模式下工作。 以下方法:

      _context.Entry<T>(entity);
      

      从上下文中获取实体。
      如果它不在其中,它将使用“Detached”EntityState 添加它(及其导航属性作为地址),**不会将它添加到“context.ChangeTracker.Entries()”**。

      您应该改用“.Add(entity)”或“.Attach(entity)”。唯一的区别是状态:

      • .Add 将使用 EntityState 添加客户及其地址:“已添加”

      • .Attach 将使用 EntityState 添加客户及其地址:“未更改”

      当您在下面的行中更改它们的状态(再次!)时,两者都将在您的代码中工作:

      _context.ApplyStateChanges();
      

      这意味着你不关心你之前应用的状态,只要它们被跟踪(不是处于分离状态)。

      我很确定代码 _context.Entry(entity) 应该是 _context.Entry(entity).State = EntityState.Modified 但无论我尝试了什么,我似乎都无法绕过这个异常。

      如果你之后不执行 ApplyStateChanges(),它更新实体和它的整个图被标记为 dirty 产生意想不到的结果(当然就像添加它的图元素一样。检查这个@987654321 @ 有完整的解释)。

      ObjectStateManager 中已存在具有相同键的对象。 ObjectStateManager 无法跟踪具有相同键的多个对象。

      这意味着您的数据库/测试中已经有相同的对象(或者更确切地说是它的导航属性之一)。 这与您的“真正”问题无关,更有可能是副作用。签入数据库的 Seed 方法,您必须有一个重复的导航实体。

      【讨论】:

        猜你喜欢
        • 2011-04-23
        • 1970-01-01
        • 2014-12-08
        • 2011-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-09
        相关资源
        最近更新 更多