Mapping a DTO to an Entity with Automapper

31,350

Solution 1

.Attach() my mapped Entity and set the state to modified?

public ActionResult EditEntity(EntityDto model)
{
    var entity = Mapper.Map<Entity>(model);
    context.Set<Entity>().Attach(entity); // (or context.Entity.Attach(entity);)
    context.Entry<Entity>(entity).State = System.Data.EntityState.Modified;
    context.SaveChanges();
    return View(model);
}

Where is your context instantiated? You should do that in your EditEntity action imo.

public ActionResult EditEntity(EntityDto model)
{
    using(var context = new MyContext())
    {
        var entity = Mapper.Map<Entity>(model);
        context.Set<Entity>().Attach(entity); // (or context.Entity.Attach(entity);)
        context.Entry<Entity>(entity).State = System.Data.EntityState.Modified;
        context.SaveChanges();
        return View(model);
    }
}

Solution 2

Try passing entity as a second parameter to your mapping.

entity = Mapper.Map<EntityDto, Entity>(model, entity);

Otherwise, your entity instance is overwritten with a new instance, and you lose the entity created in the first line.

Solution 3

An alternative answer that doesn't require Automapper for the DTO to Entity conversion is using a DbEntry:

        var oldEntity = DbSet.FirstOrDefault(x => x.Id == updatedEntity.Id);
        var oldEntry = Context.Entry(oldEntity);

        oldEntry.CurrentValues.SetValues(updatedEntity);

You don't need any attach/state checking because you are getting the old entity first so it has change tracking attached to it. Also, the CurrentValues.SetValues can accept a different type, in this example updatedEntity is the DTO. Set Values documentation is explained as such:

Sets the values of this dictionary by reading values out of the given object. The given object can be of any type. Any property on the object with a name that matches a property name in the dictionary and can be read will be read. Other properties will be ignored. This allows, for example, copying of properties from simple Data Transfer Objects (DTOs).

So seems like it already can perform in an automapper-esque way.

Share:
31,350
Paul Aldred-Bann
Author by

Paul Aldred-Bann

I am an experienced software engineer with more than 18 years’ experience of the craft and a keen interest in producing well written, scalable and reliable code. I have written code for a lot of systems still in use today with hundreds of thousands of concurrent users. I've coached, and mentored, many development teams on the nuances of writing well structured, performant and full tested code and I've created, maintained, secured and nurtured a number of end-to-end infrastructures with massive levels of resilience, redundancy, security and scaleability. In short, I've done a lot of technical stuff with a lot of technical tools and I've loved every minute of it.

Updated on July 05, 2022

Comments

  • Paul Aldred-Bann
    Paul Aldred-Bann almost 2 years

    I have an Entity Framework POCO with the following structure.

    public class Entity
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
    }
    

    I've created a Data Transfer Object for this entity to be used by my views.

    public class EntityDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    

    Now, I have the following mapping code in my Global.asax file.

    Mapper.CreateMap<Entity, EntityDto>();
    Mapper.CreateMap<EntityDto, Entity>(); // not sure whether I need this as well?
    

    Everything is working fine, I pass the DTO to my views OK and I can create a new instance of Entity from my EntityDto model. The problem arises when I try to edit my Entity; I'm aware this is down to AutoMapper losing the Entity Key that EF creates to track changes to the object, but having read through a few sources there doesn't seem to be a definitive solution. Here is the action I'm using to edit my entity.

    public ActionResult EditEntity(EntityDto model)
    {
        var entity = context.Entities.Single(e => e.Id == model.Id);
        entity = Mapper.Map<EntityDto, Entity>(model); // this loses the Entity Key stuff
        context.SaveChanges();
    
        return View(model);
    }
    

    Now, what do I do to solve this? Can I:

    1. Somehow tell AutoMapper to .Ignore() the Entity Key properties?
    2. Get AutoMapper to copy out the Entity Key properties?
    3. .Attach() my mapped Entity and set the state to modified?

    Any help always appreciated.