【问题标题】:The instance of entity type 'Item' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked无法跟踪实体类型“Item”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例
【发布时间】:2018-12-01 21:55:46
【问题描述】:

我知道已经有人问过这样的问题,但解决方案对我没有帮助。

[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();
}

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

有趣的是,即使没有从 db 中检索到任何项目,异常也是相同的,就像这样

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

【问题讨论】:

  • 尝试删除此storeContext.Entry(item).State = EntityState.Detached;,然后像您在问题末尾所做的那样手动创建项目
  • 确保单元测试中使用的DbContext storeContext 与服务中使用的实例service 相同。
  • 确保没有其他线程可以持有这个对象
  • @tomassino 一样,没有任何改变。即使只运行一个测试,错误也会保留,所以我怀疑线程。
  • @CodeNotFound 是一样的,只是包装在仓库里。

标签: c# entity-framework-core


【解决方案1】:

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;
    }
}

【讨论】:

  • 你是我的英雄
  • 这对我有用,而其他一切都没有。 EF Core 2.2 也在这里。谢了哥们!!仅供参考,我所有的 DBContext 条目都是有范围的,而不是单例的。甚至 MS 都建议使用 Scoped,但就我而言,这已经完成了。
  • 像魅力一样工作!
  • 太棒了!谢谢。
【解决方案2】:

我遇到的许多问题都有一个令人讨厌的根源。 简而言之:我已经了解了为什么 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 ;
}

我重读了错误,终于注意到了主要单词

无法跟踪实体类型“Store”的实例,因为已经在跟踪具有与 {'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 ;
}

这是 dbContext 应受范围限制的众多原因之一。 感谢hint。

【讨论】:

  • 如何在将跟踪设置为关闭时自动更新一对多关系中的子项?
  • 仅供参考,Microsoft 的文档 says:“如果您想操作实体实例并使用 SaveChanges() 将这些更改保存到数据库,则不应禁用更改跟踪。”
【解决方案3】:

Alexandar 的回答是完全禁用跟踪,解决了我的问题,但我很担心,因为我不知道这会对我的应用程序的其余部分造成什么影响。于是我去微软文档,发现this:

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

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

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

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

【讨论】:

    【解决方案4】:

    对我来说,这是解决方案:

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

    基于 Bryan 给出的解决方案。我想我使用较新版本的 EF/Automapping。这对我有用。

    【讨论】:

    【解决方案5】:

    当我通过实体框架复制数据库中的一些记录并更改作为其他实体键的一列时,我遇到了同样的问题。 跟踪模式更改没有解决问题。

    已通过在 EntityTypeConfiguration 中正确设置主键来修复此问题,以包含此处描述为 x.EntityTwoKey 的更改值。

    builder.HasKey(x => new { x.EntityOneKey, x.EntityTwoKey });
    

    【讨论】:

      【解决方案6】:

      在我的例子中,我在两个 IF 语句中运行 SaveChanges 两次时遇到了这个错误。我将 SaveChanges 移到了这两个代码块之外。只是我的服务层中的一个旁注,它正在使用 AsNoTracking() 查询数据;

      if (user.SendPaymentStatus)
      {
          user.SendPaymentStatus = false;
          saveChanges = true;
          //_userService.SaveChanges(user, false);
      
          msg = GetPaymentHTML(user.MasterNodeName, user.Payee, DbMasterNode.LastPaidUtc);
          Framework.Email.SendEmail(email, "MasterNode Payment - " + user.MasterNodeName, msg);        
      }
      
      if (user.SendNodeStatus)
      {
          user.SendNodeStatus = false;
          saveChanges = true;
          //_userService.SaveChanges(user, false);
      
          msg = GetStatusHTML(user.MasterNodeName, user.Payee, DbMasterNode.CurrentStatus, DbMasterNode.LastSeenUtc);
          Framework.Email.SendEmail(email, "MasterNode Down - " + user.MasterNodeName, msg);
      }
      
      if (saveChanges)
      {
          user.SendPaymentStatus = false;
          _userService.SaveChanges(user, false);
      }
      

      【讨论】:

        【解决方案7】:

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

        context.ChangeTracker.Clear();
        
        context.Cliente.Update(cliente);
        
        context.SaveChanges();
        

        【讨论】:

        • 如果您需要这个,您可能还会遭受糟糕的上下文生命周期管理,这也是导致问题的原因。
        【解决方案8】:

        我在尝试更新值时遇到了同样的问题。然后我发现了我使用这个的问题。

        services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("databasename")),ServiceLifetime.Singleton);
        

        然后我删除了生命周期,它对我来说效果很好。

        services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("databasename")));
        

        【讨论】:

          【解决方案9】:

          在我将主键列 Id 设置为标识列后,上述问题得到解决。

          【讨论】:

            【解决方案10】:

            我们最近在添加多个标识列 id 设置为 0 的新项目时遇到了同样的问题。我们将 OracleDataAccess 客户端用于 EF 核心 3,我们在执行 saveChanges() 时设置了新实体的序列号,但是如果已经有另一个 id=0 的项目,当我们尝试 add() 时它会出错。

            我们所做的修复是确保标识列的配置正确:

            1.) 设置密钥

            builder.HasKey(t => t.Id);
            

            2.) 正确设置数据库生成选项

            [Column("ID"), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
                public virtual int Id { get; set; }
            

            或流利的等价物:

            builder.Property(t => t.Id)
                        .ValueGeneratedOnAdd();
            

            我们没有正确执行第二步并设置为 DatabaseGeneratedOption.None,然后 EF 核心在添加时失败。

            【讨论】:

              猜你喜欢
              • 2021-11-24
              • 2020-10-16
              • 2021-08-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-01-02
              • 2023-01-03
              • 2020-01-31
              相关资源
              最近更新 更多