Home > database >  The instance of entity type 'x' cannot be tracked because another instance with the key va
The instance of entity type 'x' cannot be tracked because another instance with the key va

Time:01-05

I am working a asp .netcore 6.0 clean architecture project.

When I try to update a site, I got this error,

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

Before We use services, in there this same code worked fine. Now we move to clean architecture(CQRS and Mediatr). I used same update code but I got this error.

.AsNoTracking() I tried with var result = await _DbContext.SiteCodes.FindAsync(request.Id).AsNoTracking(); this line,

But got error as,

'ValueTask<SiteCode?>' does not contain a definition for 'AsNoTracking' and no accessible extension method 'AsNoTracking' accepting a first argument of type 'ValueTask<SiteCode?>' could be found (are you missing a using directive or an assembly reference?) [Application]

Here is my codes

UpdateSiteCommandHandler.cs

public async Task<SiteCode> Handle(UpdateSiteCommand request, CancellationToken cancellationToken)
    {
        var result = _mapper.Map<SiteCode>(request);

        _DbContext.SiteCodes.Update(result);  // goes to exception after this line

        await _DbContext.SaveChangesAsync(cancellationToken);

        return result;
    }

GetSiteByIdQueryHandler.cs

public async Task<SiteCode> Handle(GetSiteByIdQuery request, CancellationToken cancellationToken)
    {
        
        var result = await _DbContext.SiteCodes.FindAsync(request.Id);

        if (result == null)
        {
            throw new NotFoundException(nameof(SiteCode), request.Id);
        }

        return result;

    }

controller

public async Task<IActionResult> Update(int id, [FromBody] UpdateSiteCommand command)
        {
            command.Id = id;

            var siteCode = await _mediator.Send(new GetSiteByIdQuery(Id: id));

            var result = await _mediator.Send(command);
            
            return Ok(result);

        }

DependencyInjection.cs

public static class DependencyInjection
{
    public static IServiceCollection AddApplication(this IServiceCollection services)
    {
        services.AddAutoMapper(Assembly.GetExecutingAssembly());
        services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        services.AddMediatR(Assembly.GetExecutingAssembly());
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>));  // I tried with AddScoped() But not work
       
        return services;
    }

}

in DbContext

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            var entries = ChangeTracker
                .Entries()
                .Where(e => e.Entity is AuditableEntity && (
                        e.State == EntityState.Added
                        || e.State == EntityState.Modified));

            foreach (var entityEntry in entries)
            {
                if (entityEntry.State == EntityState.Added)
                {
                    ((AuditableEntity)entityEntry.Entity).CreatedAt = DateTime.UtcNow;
                    ((AuditableEntity)entityEntry.Entity).CreatedBy = _httpContextAccessor?.HttpContext?.User?.Identity?.Name ?? null;
                }
                else
                {
                    Entry((AuditableEntity)entityEntry.Entity).Property(p => p.CreatedAt).IsModified = false;
                    Entry((AuditableEntity)entityEntry.Entity).Property(p => p.CreatedBy).IsModified = false;
                }

                ((AuditableEntity)entityEntry.Entity).ModifiedAt = DateTime.UtcNow;
                ((AuditableEntity)entityEntry.Entity).ModifiedBy = _httpContextAccessor?.HttpContext?.User?.Identity?.Name ?? null;
            }

            var result = await base.SaveChangesAsync(cancellationToken)

         return result;
        }

Anyone has idea how can solve this issue?

CodePudding user response:

Try to add the method AsNoTracking() after SiteCodes to your query in Handler (GetSiteByIdQueryHandler.cs).

public async Task<SiteCode> Handle(GetSiteByIdQuery request, CancellationToken cancellationToken)
{
    
    var result = await _DbContext.SiteCodes.AsNoTracking().FindAsync(request.Id);

    if (result == null)
    {
        throw new NotFoundException(nameof(SiteCode), request.Id);
    }

    return result;
}

CodePudding user response:

EF Core uses ChangeTracker to detect changes in loaded entities, and better (not faster) solution is to load entity for update.

public async Task<SiteCode> Handle(UpdateSiteCommand request, CancellationToken cancellationToken)
{
    // the following code will load entitiy if it is still not loaded.
    var dbRequest = await _DbContext.SiteCodes.FindAsync(request.Id);

    if (dbRequest == null)
        throw new Exception("Not found");

    _mapper.Map<SiteCode>(request, dbRequest);

    await _DbContext.SaveChangesAsync(cancellationToken);

    return result;
}
  •  Tags:  
  • Related