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

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

问题描述

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


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


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



服务.AddDbContext(options =>

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

已设置作用域的生存期默认情况下,但我写起来更容易理解。



已获得 Anomaly 对象像这样:

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

公共异步任务;异常> GetAnomaly(int anomalyId,用户)
{
var anomaly = await GetAll()
.FirstOrDefaultAsync(a => a.Id == anomalyId);

返回异常;
}

Update()方法如下:

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

transaction.Commit();
}
catch(例外)
{
transaction.Rollback();
投掷;
}
}

此交易前包含一些支票,但在这种情况下没有足够的相关性。



这是我收到的错误,因为实例已被跟踪。我不明白这是怎么发生的..如果上下文是 Scoped ,则


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


如果我在PUT请求上的上下文与GET请求的上下文不同,那么如何跟踪实体?



使其工作的唯一方法是设置 ChangeTracker EntityState.Detached 。然后它起作用了..但这至少没有意义,至少就我目前所知。。



我发现

解决方案

默认情况下,当您检索被跟踪的实体时,由于已跟踪它们,因此您可以仅调用SaveChanges而不调用Update。您还可以通过使用.AsNoTracking()



检索实体而不跟踪它们,如果尚未跟踪,则需要更新,因此,如果您使用AsNoTracking,那么您确实需要使用Update在SaveChanges之前

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

公共异步任务<异常> GetAnomaly(int anomalyId,用户)
{
var anomaly =等待GetAll()
.AsNoTracking()
.FirstOrDefaultAsync(a => a.Id == anomalyId) ;

返回异常;
}

您还可以检查是否跟踪该实体以知道是否调用Update或不是:

 使用(var transaction = _context.Database.BeginTransaction())
{
try
{

布尔跟踪= _context.ChangeTracker.Entries< Anomaly>()。Any(x => x.Entity.Id == anomaly.Id);
if(!tracking)
{
_context.Anomalies.Update(anomaly);
}

_context.SaveChanges();

transaction.Commit();
}
catch(例外)
{
transaction.Rollback();
投掷;
}
}


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天全站免登陆