无法跟踪实体类型的实例,因为已经跟踪了具有相同键的此类型的另一个实例

我有一个服务对象Update

 public bool Update(object original, object modified) { var originalClient = (Client)original; var modifiedClient = (Client)modified; _context.Clients.Update(originalClient); //<-- throws the error _context.SaveChanges(); //Variance checking and logging of changes between the modified and original } 

这是我从以下方法调用此方法的地方:

 public IActionResult Update(DetailViewModel vm) { var originalClient = (Client)_service.GetAsNoTracking(vm.ClientId); var modifiedClient = (Client)_service.Fetch(vm.ClientId.ToString()); // Changing the modifiedClient here _service.Update(originalClient, modifiedClient); } 

这是GetAsNotTracking方法:

 public Client GetAsNoTracking(long id) { return GetClientQueryableObject(id).AsNoTracking().FirstOrDefault(); } 

Fetch方法:

 public object Fetch(string id) { long fetchId; long.TryParse(id, out fetchId); return GetClientQueryableObject(fetchId).FirstOrDefault(); } 

GetClientQueryableObject

 private Microsoft.Data.Entity.Query.IIncludableQueryable GetClientQueryableObject(long searchId) { return _context.Clients .Where(x => x.Id == searchId) .Include(x => x.Opportunities) .ThenInclude(x => x.BusinessUnit) .Include(x => x.Opportunities) .ThenInclude(x => x.Probability) .Include(x => x.Industry) .Include(x => x.Activities) .ThenInclude(x => x.User) .Include(x => x.Activities) .ThenInclude(x => x.ActivityType); } 

有任何想法吗?

我查看了以下文章/讨论。 无济于事: ASP.NET GitHub问题3839

更新:

以下是GetAsNoTracking的更改:

 public Client GetAsNoTracking(long id) { return GetClientQueryableObjectAsNoTracking(id).FirstOrDefault(); } 

GetClientQueryableObjectAsNoTracking

 private IQueryable GetClientQueryableObjectAsNoTracking(long searchId) { return _context.Clients .Where(x => x.Id == searchId) .Include(x => x.Opportunities) .ThenInclude(x => x.BusinessUnit) .AsNoTracking() .Include(x => x.Opportunities) .ThenInclude(x => x.Probability) .AsNoTracking() .Include(x => x.Industry) .AsNoTracking() .Include(x => x.Activities) .ThenInclude(x => x.User) .AsNoTracking() .Include(x => x.Activities) .ThenInclude(x => x.ActivityType) .AsNoTracking(); } 

如果不覆盖EF轨道系统,您还可以在保存之前分离“本地”条目并附加更新的条目:

 // var local = _context.Set() .Local .FirstOrDefault(entry => entry.Id.Equals(entryId)); // check if local is not null if (!local.IsNull()) // I'm using a extension method { // detach _context.Entry(local).State = EntityState.Detached; } // set Modified flag in your entry _context.Entry(entryToUpdate).State = EntityState.Modified; // save _context.SaveChanges(); 

更新:为避免代码冗余,您可以执行扩展方法:

 public static void DetachLocal(this DbContext context, T t, string entryId) where T : class, IIdentifier { var local = context.Set() .Local .FirstOrDefault(entry => entry.Id.Equals(entryId)); if (!local.IsNull()) { context.Entry(local).State = EntityState.Detached; } context.Entry(t).State = EntityState.Modified; } 

我的IIdentifier接口只有一个Id字符串属性。

无论您的实体是什么,您都可以在您的上下文中使用此方法:

 _context.DetachLocal(tmodel, id); _context.SaveChanges(); 

听起来你真的只是想跟踪对模型所做的更改,而不是在内存中实际保留未跟踪的模型。 我是否可以建议一种可以完全解决问题的替代方法?

EF将自动跟踪您的变化。 如何利用内置的逻辑?

在DbContext中使用Ovverride SaveChanges()

  public override int SaveChanges() { foreach (var entry in ChangeTracker.Entries()) { if (entry.State == EntityState.Modified) { // Get the changed values. var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties(); var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues; foreach (var propName in modifiedProps) { var newValue = currentValues[propName]; //log changes } } } return base.SaveChanges(); } 

很好的例子可以在这里找到:

entity framework6:审计/跟踪变更

使用MVC和entity framework实现审核日志/更改历史记录

编辑: Client可以轻松更改为界面。 让我们说ITrackableEntity 。 这样,您可以集中逻辑并自动将所有更改记录到实现特定接口的所有实体。 界面本身没有任何特定属性。

  public override int SaveChanges() { foreach (var entry in ChangeTracker.Entries()) { if (entry.State == EntityState.Modified) { // Same code as example above. } } return base.SaveChanges(); } 

另外,看看eranga的一个很好的建议是订阅而不是实际覆盖SaveChanges()。