【问题标题】:EF Core 'another instance is already being tracked'EF Core '另一个实例已被跟踪'
【发布时间】:2019-05-21 14:41:13
【问题描述】:

我在使用 EF Core 2.2.3 更新 .Net Core 2.2.0 中的实体时遇到问题。

保存更改时出错。错误详情: 无法跟踪实体类型“资产”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用

DB Context 是这样注册的:

services.AddDbContext(options =>

options.UseSqlServer(Configuration.GetConnectionString("DbConnection")), ServiceLifetime.Scoped);

Scoped 生命周期是默认设置的,但我写它是为了更容易理解。

Anomaly 对象是这样得到的:

public IQueryable<Anomaly> GetAll()
    {return _context.Anomalies.Include(a => a.Asset).Include(a => a.Level)
}

public async Task<Anomaly> GetAnomaly(int anomalyId, User user)
{
    var anomaly = await GetAll()
        .FirstOrDefaultAsync(a => a.Id == anomalyId);

    return anomaly;
}

Update() 方法看起来像这样:

using (var transaction = _context.Database.BeginTransaction())
{
    try
    {
        _context.Anomalies.Update(anomaly);
        _context.SaveChanges();

        transaction.Commit();
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        throw;
    }
}

它在此交易之前包含一些检查,但在此上下文中没有足够的相关性。

这是我得到错误的地方,实例已经被跟踪。我不明白这是怎么发生的..如果上下文是Scoped,那么

...“将为每个范围创建一个新的服务实例”,在这种情况下,为每个请求

如果我的 PUT 请求上下文与 GET 请求的上下文不同,那么如何跟踪实体?这在最基本的层面上是如何工作的?

使其工作的唯一方法是设置从ChangeTrackerEntityState.Detached 的所有条目的状态。然后它起作用了..但它没有任何意义,至少就我目前的知识而言..

我找到了this question,但没有有效的答案,只有关于 EF 如何进行跟踪的变通方法和假设。


更新 这是一个指向 bitbucket 的链接,其中包含重现此问题的示例:EF Core Update Sample

我序列化了从上下文中检索到的对象。

左侧有跟踪 右侧没有跟踪

【问题讨论】:

  • 每个请求有超过 1 个上下文实例。正如你所提到的。使用 Detach 不是一个有效的解决方案,您需要找到根本原因。你如何注入 _context 类型?
  • “它在此交易之前包含一些检查,但在此上下文中没有足够的相关性。” 你确定吗? _context.Set&lt;Asset&gt;().Local.Count == 0 调用_context.Anomalies.Update(anomaly);之前是真的吗?
  • @Ivan,看来_context.Set&lt;Asset&gt;().Local.Count == 0false。我最终在检查之前评论了所有代码,但它仍然是错误的..
  • @ilkerkaran,我在构造函数中注入_context,如下所示:public AnomalyManager(SAMSDbContext context){_context = context;} 其中_contextprivate readonly 字段。在最初的问题中,我发布了 DbContext 如何在 Startup.cs 中注册

标签: c# entity-framework asp.net-core .net-core entity-framework-core


【解决方案1】:

默认情况下,当您检索实体时,它们会被跟踪,并且由于它们被跟踪,您可以只调用 SaveChanges 而不是调用 Update。您还可以使用 .AsNoTracking() 检索实体而不跟踪它们

如果尚未跟踪,则需要调用 Update,因此如果您使用 AsNoTracking,则确实需要在 SaveChanges 之前使用 Update

public IQueryable<Anomaly> GetAll()
{    return _context.Anomalies
    .Include(a => a.Asset)
    .Include(a => a.Level);
}

public async Task<Anomaly> GetAnomaly(int anomalyId, User user)
{
    var anomaly = await GetAll()
        .AsNoTracking()
        .FirstOrDefaultAsync(a => a.Id == anomalyId);

    return anomaly;
}

您还可以检查实体是否被跟踪以了解是否调用更新:

using (var transaction = _context.Database.BeginTransaction())
{
    try
    {

        bool tracking = _context.ChangeTracker.Entries<Anomaly>().Any(x => x.Entity.Id == anomaly.Id);
        if (!tracking)
        {
            _context.Anomalies.Update(anomaly);
        }

        _context.SaveChanges();

        transaction.Commit();
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        throw;
    }
}

【讨论】:

  • 我试过了,但没有用。即使在`Include() 之前使用.AsNoTracking(),即使在它之后,同样的结果..
  • 奇怪它对我有用,我已经更新了我的答案以显示如何检测实体是否被跟踪,这可用于决定是否调用 Update。
  • 我已经编辑了答案,看来 .AsNoTracking 应该是 after all the .Includes
  • 我检查了那个问题,它对我不起作用。我不确定是因为 .NET Core,还是为什么.. 也尝试了 QueryTrackingBehavior 字段,没有任何改进。我开始相信在另一个层面上出了点问题,也许我的设置有问题,我的配置或类似的东西..当我发现一些东西时会更新
  • 我认识 Joe,我删除了 Update 调用,但没有成功。没有它,任何更改都不会到达数据库..
【解决方案2】:

我试图在没有注意到的情况下更新相同的数据。我花了十个小时才意识到这一点。如果您有像我这样的重复值,我建议删除该数据...阅读此答案的人可能已经像我一样尝试了互联网上的所有解决方案,破坏了项目,卡住了相同的错误,并且 省略了重复 数据就像我一样。我的朋友,你并不孤单。

model.GroupBy(gb => gb.ID).Select(s=>s.First()).ToList();//remove duplicates!!!!!

【讨论】:

  • 好建议,谢谢!似乎这将是我们在某个时候都面临的问题的可能解决方案的集合。
【解决方案3】:

所以,最后我们最终使用了一个自定义的 UpdateEntity 方法来保存我们对某些实体的更改。该方法遍历实体的每个属性、导航属性和集合属性,确保没有链,并且一次更新对象。

它适用于大物体,我们只在这些情况下使用它。对于简单的操作,我们继续使用简单的更新

Here is the link to a bitbucket repository with the source code

要使用此方法,您必须先获取 db 实体。然后使用请求接收到的 db 实体和您的实体调用 UpdateEntity 方法。

我希望它能帮助你,就像它帮助了我一样。干杯!

【讨论】:

    【解决方案4】:

    我遇到了同样的问题,但尝试向同一个实体添加第二行。就我而言,这是因为我假设主键是一个 Int Identity 并自动生成。 由于我没有为其分配任何值,因此第二行具有相同的 Id,这是 cero 默认值。 吸取的教训,当您在 EF 中的 Add() 操作期间看到此错误时,请确保您的密钥是 IDENTITY 如果这是您的意图。

    【讨论】:

      【解决方案5】:

      在我的情况下,问题来自于在 1:many 关系中更新列表时缺少类定义中的实体。

      public class Exercise
      {
        public Guid Id { get; set; } 
        //other props...
        public List<EntityInExercise> Entities { get; set; } = new();
      }
      
      public class EntityInExercise
      {
        public Guid Id { get; set; } 
        //other props...
      
        //?this line was missing ?
        public Exercise Exercise{ get; set; }= null!;
        //☝️
      }
      

      在这两种情况下(有和没有缺失行)它都会创建相同的数据库。我遇到的唯一问题是尝试使用 Automapper 及其 Collections 更新该列表时。

      【讨论】:

      • 不清楚这将如何导致此错误,这与重复的主键有关。引用绝不是(部分)主键。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多