EF Core '另一个实例已被跟踪' [英] EF Core 'another instance is already being tracked'

查看:27
本文介绍了EF Core '另一个实例已被跟踪'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<块引用>

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

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

services.AddDbContext(options =>

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

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

Anomaly 对象是这样得到的:

public IQueryable得到所有(){return _context.Anomalies.Include(a => a.Asset).Include(a => a.Level)}公共异步任务<异常>GetAnomaly(int anomalyId, 用户用户){var 异常 = 等待 GetAll().FirstOrDefaultAsync(a => a.Id == anomalyId);返回异常;}

Update() 方法如下所示:

using (var transaction = _context.Database.BeginTransaction()){尝试{_context.Anomalies.Update(异常);_context.SaveChanges();交易.提交();}捕获(异常前){事务.回滚();扔;}}

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

这是我收到实例已经被跟踪的错误的地方.我无法理解这是如何发生的.. 如果上下文是 Scoped,那么

<块引用>

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

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

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

我发现

解决方案

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

检索实体而不跟踪它们

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

public IQueryable得到所有(){ 返回 _context.Anomalies.Include(a => a.Asset).include(a => a.Level);}公共异步任务<异常>GetAnomaly(int anomalyId, 用户用户){var 异常 = 等待 GetAll().AsNoTracking().FirstOrDefaultAsync(a => a.Id == anomalyId);返回异常;}

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

using (var transaction = _context.Database.BeginTransaction()){尝试{bool tracking = _context.ChangeTracker.Entries().Any(x => x.Entity.Id == anomaly.Id);如果(!跟踪){_context.Anomalies.Update(异常);}_context.SaveChanges();交易.提交();}捕获(异常前){事务.回滚();扔;}}

I have a problem updating an entity in .Net Core 2.2.0 using EF Core 2.2.3.

An error occurred while saving changes. Error details: The instance of entity type 'Asset' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using

This is how the DB Context is registered:

services.AddDbContext(options =>

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

The Scoped lifetime is set by default but I wrote it to be more easy to understand.

The Anomaly object is got like this:

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

And the Update() method looks like this:

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

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

It contains some checks before this transaction, but none relevant enough in this context.

This is where I get the error with instance already being tracked. I can't understand how this happens .. If the context is Scoped, then

... "a new instance of the service will be created for each scope", in this case, for each request

If my context on the PUT request is different from the context of the GET request, how is the entity already being tracked? How does this work at the most basic levels?

The only way to make it work is to set the state for all entries from the ChangeTracker to EntityState.Detached. Then it works.. but it makes no sense, at least to my current knowledge..

I found this question but with no valid answer, only with workarounds and assumptions about how EF does the tracking.


UPDATE Here is a link to bitbucket with a sample recreating this problem: EF Core Update Sample

I serialized the objects retrieved from the context.

With Tracking on the LEFT <====> With NO tracking on the RIGHT

解决方案

By default when you retrieve entities they are tracked and since they are tracked you could just call SaveChanges and not call Update. You can also retrieve entities without tracking them by using .AsNoTracking()

calling Update is needed if not tracked already, so if you use AsNoTracking then you do need to use Update before SaveChanges

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

You can also check if the entity is tracked to know whether to call Update or not:

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

这篇关于EF Core '另一个实例已被跟踪'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆