When I want to update an entity, I face the following error:
Message = "The instance of entity type 'Vehicle' 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. Conside...
I have added the database as addScope. I think the problem is during Mapping because when I manually fill the Entity fields with the sent request, it works without any problem. My question is how to map the properties? Each of my Entity has a lot of properties and it is difficult to manually enter all these working values.
Update method:
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult> UpdateVehicle(UpdateVehicleCommand command)
{
if (string.IsNullOrEmpty(command.Id))
{
return BadRequest();
}
try
{
var result = await Mediator.Send(command);
return Ok(result);
}
catch (Exception e)
{
_logger.LogError("Can not Update Vehicle because : {e}", e.Message);
return BadRequest();
}
}
in UpdateVehicleCommand :
public async Task<Unit> Handle(UpdateVehicleCommand request, CancellationToken cancellationToken)
{
var entity = await _context.Vehicles
.FindAsync(new object[] { request.Id }, cancellationToken);
if (entity == null)
{
throw new NotFoundException(nameof(Vehicle), request.Id);
}
// *********** Mapping ****************
// ****The problem is in the following line****
_context.Vehicles.Update(_mapper.Map<Domain.Entities.Vehicle.Vehicle>(request));
***********Import in manual mode************
//entity.ImageDocument = request.ImageDocument;
//entity.ImageFrontCard = request.ImageFrontCard;
//entity.ImageBackCard = request.ImageBackCard;
.....
.....
.....
await _context.SaveChangesAsync(cancellationToken);
return Unit.Value;
}
Profile settings for automapper are also added correctly
CodePudding user response:
This is one of the main reasons I dislike Entity Framework for RESTful API development: The Unit Of Work pattern is not for the majority of your needs when programming REST. You are being bitten by one of the so-called "features" of Entity Framework: Entity tracking. I don't think I can tell you exactly where your problem is because it is actually dependant on the workflow your server follows, but I can give you the general rule of thumb: Disable tracking for your DbSet<>
's whenever you are getting data.
As for a long-term solution: Dump EF. Move to Dapper, even if only for querying.
CodePudding user response:
The issue has nothing to do with Automapper and EF is doing what you are telling it to do, which is not correct:
var entity = await _context.Vehicles .FindAsync(new object[] { request.Id }, cancellationToken);
This loads an entity with Id equal to request.Id
into the Context.
_context.Vehicles.Update(_mapper.Map<Domain.Entities.Vehicle.Vehicle>(request));
And this line attaches a new entity with the same id to the context. Therefore, EF complains that there is already one entity with that Id.
There are multiple ways to implement this. I can give you a couple:
Instead of loading the entity, just check if it exists, for example with AnyAsync(). If it returns
false
, throw an exception.Load the entity as you do now, but instead of attaching a new entity (with the Update method), use automapper to map the values from the request to the
entity
object that you loaded. There is no need to callcontext.Vehicles.Update
because the entity is already being tracked by EF. If you change any property and then callcontext.SaveChangesAsync
, the changes will be saved.