简体   繁体   中英

The instance of entity type cannot be tracked even with AsNoTracking

I receive an error in test environment, but not on the prod environment even as no tracking has already been placed.

Error "GetUserAsync: System.InvalidOperationException: The instance of entity type 'UserEntity' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

Code:

internal async Task<UserEntity> GetUserAsync(CancellationToken cancellationToken)
{
    var result = await _userDataQuery.GetLatestUser(cancellationToken);
}

I already added AsNoTracking :

public async Task<UserEntity> GetLatestUser(CancellationToken cancellationToken)
{
    var result = await this.Entities
                           .OrderByDescending(m => m.Id)
                           .AsNoTracking().FirstOrDefaultAsync();
    await Context.Entry(result).ReloadAsync();

    return result;
}

Not sure if this help but the I also notice the pg_sequences on postgreSQL between the two environments are different for User Table and both of them only have 1 record in the table

Test Environment:

last_value = 2

Prod Environment:

last_value = 1

This error means that within the lifetime scope of the DbContext (Context) that entity has already been loaded and is being tracked. This could be directly via a different method call, or indirectly by being eager or lazy loaded as part of another entity load. Reload will attempt to track the entity which will result in an error where another matching instance is already tracked.

From what I can make of what you are trying to do, you probably don't want to use AsNoTracking() since you cannot guarantee that the entity hasn't already been tracked. Load it as-per normal.

var result = await this.Entities
                       .OrderByDescending(m => m.Id)
                       .FirstAsync();
await Context.Entry(result).ReloadAsync();

Whenever you use an *OrDefault() flavour, you must handle the fact that there may be no items in the set. If you expect at least one, use First .

The downside here is that Reload will be called even when the desired item is already freshly loaded from the DB. Since we cannot assume the desired item is or isn't already tracked, about the best option would probably be:

var id = this.Entities
             .OrderByDescending(m => m.Id)
             .Select(x => x.Id)
             .FirstAsync();

var result = this.Entities.Local
                       .Where(m => m.Id == id)
                       .SingleOrDefault();
if (result != null)
    await Context.Entry(result).ReloadAsync();
else
    result = this.Entities
        .Where(m => m.Id == id)
        .Single();

This code looks for a particular item in the local tracking cache, if found it will reload it, otherwise it will load the current item from the DB.

This approach (minus selecting the ID from the DB) would apply in any other situations where you know ahead of time what specific entity you want rather than relying on something like FirstOrDefault and want to ensure any already tracked entity is available and refresh it.

Just check below things.

  1. whether you're doing context.Update( or context.Entity.Update( anywhere in the code. (this might cause such issues)
  2. Make sure to not hold the DbContext instance for too long. By MS recommendation they should be either Transient or Scoped (and not singleton).

If you're doing context.Update( anywhere in the code and holding the DbContext instance for considerably long time, there will be high chances you're trying to update the same tracked entity multiple times in the DbContext lifetime.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM