AutoMapper Update Actions in ASP.NET MVC

那年仲夏 提交于 2019-11-30 10:30:36

I use the following approach:

[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
    User user = _userRepository.FindById(uvm.Id);

    user.Forename = uvm.Forename;
    user.Surname = uvm.Surname;
    user.EmailAddress = uvm.EmailAddress;

    _userRepository.Update(user);

    return RedirectToAction("Index");
}

UPDATE:

To address the comments about AutoMapper here's how to proceed:

Let's take for example the following classes:

public class UserViewModel
{
    public string Forename { get; set; }
    public string Surname { get; set; }
    public string EmailAddress { get; set; }
}

public class User
{
    public string Forename { get; set; }
    public string Surname { get; set; }
    public string EmailAddress { get; set; }

    public string Password { get; set; }
}

We don't want to modify the user password in the UI. So we express our intention to AutoMapper:

Mapper
    .CreateMap<UserViewModel, User>()
    .ForMember(dest => dest.Password, opt => opt.Ignore());

and then:

[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
    // Fetch the original model we would like to update
    User user = _userRepository.FindById(uvm.Id);

    Mapper.Map(uvm, user);
    // At this stage the user model will have its 
    // Forename, Surname and EmailAddress properties 
    // updated from the view model and its Password property
    // will remain the one we got from the repository

    _userRepository.Update(user);

    return RedirectToAction("Index");
}

UPDATE 2:

To address the question in the comments about configuring AutoMapper I usually use Profiles:

public class UsersProfile : Profile
{
    protected override void Configure()
    {
        Mapper
            .CreateMap<UserViewModel, User>()
            .ForMember(dest => dest.Password, opt => opt.Ignore());    

        Mapper
            .CreateMap<User, UserViewModel>();
    }
}

and then have a registry class which registers all the mappers:

public class MappingsRegistry
{
    public static void Configure()
    {
        Mapper.AddProfile(new UsersProfile());
        Mapper.AddProfile(new SomeOtherProfile());
        ...
    }
}

which is called in Application_Start:

MappingsRegistry.Configure();

Finally my controllers have a reference to the mapping engine:

public class UsersController : Controller
{
    private readonly IUsersRepository _repository;
    private readonly IMappingEngine _mappingEngine;
    public ContratsFCController(IUsersRepository repository, IMappingEngine mapperEngine)
    { 
        _repository = repository;
        _mapperEngine = mapperEngine;
    }

    [AutoMap(typeof(User), typeof(UserViewModel))]
    public ActionResult Update(int id)
    {
        var user = _repository.FindById(id);
        return View(user);
    }

    [HttpPost]
    public ActionResult Update(UserViewModel uvm)
    {
        if (!ModelState.IsValid)
        {
            return View(uvm);
        }
        var user = _repository.FindById(uvm.Id);
        _mapperEngine.Map(uvm, user);
        _repository.Update(user);
        return RedirectToAction("Index");
    }
}

Now all that's left is to instruct your DI framework to pass the Mapper.Engine property to the constructor and in your unit tests obviously substitute them with an appropriate mock.

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