Mapping a DTO to an Entity with Automapper

心不动则不痛 提交于 2019-12-17 15:46:41

问题


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.


回答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);
    }
}



回答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.




回答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.



来源:https://stackoverflow.com/questions/13156437/mapping-a-dto-to-an-entity-with-automapper

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!