首页 > 解决方案 > EF Core '另一个实例已被跟踪'

问题描述

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

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

这是数据库上下文的注册方式:

services.AddDbContext(选项 =>

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 请求的上下文不同,那么如何跟踪实体?这在最基本的层面上是如何运作的?

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

我发现了这个问题,但没有有效的答案,只有关于 EF 如何进行跟踪的变通方法和假设。


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

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

左侧有跟踪 <====> 右侧没有跟踪 左侧有跟踪 <====> 右侧没有跟踪

标签: c#entity-frameworkasp.net-core.net-coreentity-framework-core

解决方案


默认情况下,当您检索实体时,它们会被跟踪,并且由于它们被跟踪,您可以只调用 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;
    }
}

推荐阅读