无法跟踪实体类型“ Item”的实例,因为已经跟踪了具有相同键值“ {'Id”}的另一个实例

Ale*_*nko 6 c# entity-framework-core

我知道已经提出了这样的问题,但是解决方案并没有帮助我。

[Fact]
public async Task UpdateAsync()
{
    string newTitle = "newTitle1";
    int newBrandId = 3;
    var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
    item.BrandId = newBrandId;
    item.Title = newTitle;
    storeContext.Entry(item).State = EntityState.Detached;
    await service.UpdateAsync(item); // exception inside
    var updatedItem = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
    Assert.Equal(newTitle, updatedItem.Title);
    Assert.Equal(newBrandId, updatedItem.BrandId);
}

public async Task UpdateAsync(T entity)
{
    _dbContext.Entry(entity).State = EntityState.Modified; // exception when trying to change the state
    await _dbContext.SaveChangesAsync();
}
Run Code Online (Sandbox Code Playgroud)

消息:System.InvalidOperationException:无法跟踪实体类型'Item'的实例,因为已经跟踪了另一个具有相同'{'Id'}键值的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'来查看冲突的键值。

有趣的是,即使没有任何项目从数据库撤回,异常也是一样的,就像这样

//var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
  var item = new Item()
  {
      Id = 1,
      BrandId = newBrandId,
      CategoryId = 1,
      MeasurementUnitId = 1,
      StoreId = 1,
      Title = newTitle
  };
Run Code Online (Sandbox Code Playgroud)

小智 21

EF core 2.2 也有同样的问题。我从未在其他应用程序中遇到过这种情况。

最终以某种方式重写了我所有的更新功能:

public bool Update(Entity entity)
{
    try
    {   
       var entry = _context.Entries.First(e=>e.Id == entity.Id);
       _context.Entry(entry).CurrentValues.SetValues(entity);
       _context.SaveChanges();
       return true;
    }
    catch (Exception e)
    {
         // handle correct exception
         // log error
         return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你是我的英雄 (4认同)
  • 当其他一切都不起作用时,这对我有用。EF Core 2.2 也在这里。谢了哥们!!仅供参考,我的所有 DBContext 条目都是有范围的,而不是单例。甚至 MS 也建议使用 Scoped,但就我而言,这已经完成了。 (2认同)
  • 奇迹般有效! (2认同)

Ask*_*ous 8

亚历山大的答案是完全禁用跟踪,解决了我的问题,但我很担心,因为我不知道这会对我的应用程序的其余部分产生什么影响。所以我去微软文档并发现了这个

如果您想要操作实体实例并使用 SaveChanges() 将这些更改保存到数据库,则不应禁用更改跟踪

此方法为使用这些选项创建的所有上下文设置默认行为,但您可以使用 QueryTrackingBehavior 覆盖上下文实例的此行为,或者使用 AsNoTracking (IQueryable)和 AsTracking(IQueryable) 方法覆盖单个查询的此行为。

所以我的解决方案是仅在需要时禁用跟踪。因此,我通过在代码的其他部分使用它来从数据库检索相同的条目来解决我的问题:

var entry = await context
    .SomeDbTable
    .AsNoTracking() // this is what you're looking for
    .Find(id);
Run Code Online (Sandbox Code Playgroud)


use*_*040 6

For me was this the solution:

public void Update(int id, T obj)
        {
            var entry = table.Find(id);
            _context.Entry(entry).CurrentValues.SetValues(obj);
        }
Run Code Online (Sandbox Code Playgroud)

Based on the solution Bryan gave. I think I use newer version of EF/Automapping. This works for me.


小智 6

当我想更新数据时,我遇到了一些类似的错误,我发现可以通过清除属性上下文来修复它。这是 a 所做的。这不是同一个问题,但它是同一错误,所以我认为可以用同样的方式修复它。清除上下文似乎是一个很好的解决方案,因为这是发生事情的原因。

context.ChangeTracker.Clear();

context.Cliente.Update(cliente);

context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)


Ale*_*nko 5

我遇到的许多问题都有一个令人讨厌的根源。简而言之:我已经了解了为什么dbContext是作用域而不是单例的艰难方法。这是商店类型,但问题是相同的。这是简化的测试初始化​​代码

public TestBase()
{
    services = new ServiceCollection();
    storeContext = StoreContextMock.ConfigureStoreContext(services, output);
    serviceProvider = services.BuildServiceProvider();
}
public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
    services.AddDbContext<StoreContext>(c =>
        c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

    var serviceProvider = services.BuildServiceProvider();
    var storeContext = serviceProvider.GetRequiredService<StoreContext>();
    storeContext .Stores.Add(new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "john@mail.com" });
    storeContext .Stores.Add(new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "jennifer@mail.com" });
    storeContext .SaveChanges();
    return storeContext ;
}
Run Code Online (Sandbox Code Playgroud)

我重读了错误,最后注意到了主词

实例的实体类型“商店”的无法被追踪,因为与{“ID”}相同键值的另一个实例已被跟踪

因此,必须有一些孤立的跟踪实例阻止我与商店合作。我没有保存对s1或的任何引用s2,因此storeContext即使离开声明和初始化的范围,它也必须在插入的对象上存储引用。这就是为什么我无法正常更新变量的原因,也是为什么我从db对象中“查询”的所有导航属性都被分配了(延迟加载与此无关)。以下代码解决了我所有的问题。

public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
    services.AddDbContext<StoreContext>(c =>
        c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

    var serviceProvider = services.BuildServiceProvider();
    var storeContext = serviceProvider.GetRequiredService<StoreContext>();
    var s1 = new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "john@mail.com" };
    var s2 = new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "jennifer@mail.com" }
    storeContext .Stores.Add(s1);
    storeContext .Stores.Add(s2);
    storeContext .Entry<Store>(s1).State = EntityState.Detached;
    storeContext .Entry<Store>(s2).State = EntityState.Detached;
    storeContext .SaveChanges();
    return storeContext ;
}
Run Code Online (Sandbox Code Playgroud)

这是dbContext应该受作用域限制的众多原因之一。感谢您的提示