简体   繁体   中英

Use AsNoTracking for entities mapped from AutoMapper

I have a situation where I am mapping DTO -> Database Entity using automapper.

var entityObj = _mapper.Map<REQUESTEXT>(reqDTO);

Then I am using entityObj to update the record in the database.

void Update(REQUESTEXT entityObj)
{
    _context.REQUESTEXTs.Attach(entityObj);  <--- Error
    _context.Entry(entityObj).Property(x => x.CUSTOPTIONCD).IsModified = true;
    _context.SaveChanges();
}

When i am trying to attach REQUESTEXT object to context, its giving me an error:

Attaching an entity of type 'A' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

As per this SO answer: https://stackoverflow.com/a/23228001/1169180 I need to use AsNoTracking() , I am not sure how to use that in AutoMapper ?

Any suggestions?

AsNoTracking refers to when the entities are loaded by the context, not Automapper. You are getting the error because at some point in that DbContext's life, it has loaded the entity with that ID and is tracking it. The option they recommended is to change over your entity loading to use AsNoTracking which effectively tells EF not to track the entity when it is read.

An alternative solution to that problem is to check for the existence of the entity in the DbContext's local cache first, and if found, use AutoMapper to map your property changes across to that existing entity, rather than creating a new entity.

For example:

var existingEntity = _context.REQUESTEXTs.Local.SingleOrDefault(x => x.EntityId == reqDTO.EntityId);
if(existingEntity != null)
    mapper.Map(reqDto, existingEntity);
else
{
    var entityObj = _mapper.Map<REQUESTEXT>(reqDTO);    
    _context.REQUESTEXTs.Attach(entityObj);
    _context.Entry(entityObj).Property(x => x.CUSTOPTIONCD).IsModified = true;
} 
_context.SaveChanges();

This checks the local cache for an existing entity, (does not hit DB) and if found, it uses AutoMapper to update it's properties. The entity tracking will note the changes, so when SaveChanges is called, the modifications would go through to the DB. If the local cache does not have the entity, then we create a new instance, attach it, mark it as modified, and save.

One suggestion that appears to be missing in your example: You should be validating the assumptions that:

  • The ID in your DTO actually does exist in the database before attempting this and
  • The record being modified can, and should be editable by the user making this request. and
  • The data being updated is fully validated.

If this is a web application /w an accessible Controller action or Web API endpoint, this could be exploitable to allow users to edit records they otherwise should not be able to, or update records in ways they should not be. (Trust nothing from a client request.) Each request should be validated thoroughly, and any deviation detected should terminate the client session.

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