实体框架条目更改跟踪问题
Posted
技术标签:
【中文标题】实体框架条目更改跟踪问题【英文标题】: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<T>(entity)
与_context.Entry<T>(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<IObjectState>()
时,它不会将新添加的地址作为已被 ChangeTracker 更改的条目返回。
我很确定代码 _context.Entry<T>(entity)
应该是 _context.Entry<T>(entity).State = EntityState.Modified
但无论我尝试了什么,我似乎都无法绕过这个异常。
我将 EntityFramework 5.0 与 .NET 4.0 一起使用,这意味着 EntityFramework 版本是 4.4。我错过了什么?我已经阅读了我能找到的关于这个主题的每一篇文章,这看起来应该有效。我从 Julie Lerman 的 PluralSight 视频 Entity Framework in the Enterprise 中获得了这个状态变化跟踪代码。
任何帮助我指出正确的方向将不胜感激。
【问题讨论】:
【参考方案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 方法,您必须有一个重复的导航实体。
【讨论】:
以上是关于实体框架条目更改跟踪问题的主要内容,如果未能解决你的问题,请参考以下文章
为啥实体框架 System.Data.Entity.Core.Objects.RelationshipEntry 错误? (用于更改跟踪)