Я использую следующий подход:
[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:
Чтобы ответить на комментарии о AutoMapper
, вот как это сделать:
Давайте возьмем для примера следующие классы:
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; }
}
Мы не хотим изменять пароль пользователя в пользовательском интерфейсе. Поэтому мы выражаем наше намерение AutoMapper:
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
и затем:
[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");
}
ОБНОВЛЕНИЕ 2:
Для решения вопроса в комментариях по настройке AutoMapper я обычно использую Профили:
public class UsersProfile : Profile
{
protected override void Configure()
{
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
Mapper
.CreateMap<User, UserViewModel>();
}
}
и затем есть класс реестра, который регистрирует все преобразователи:
public class MappingsRegistry
{
public static void Configure()
{
Mapper.AddProfile(new UsersProfile());
Mapper.AddProfile(new SomeOtherProfile());
...
}
}
который называется в Application_Start
:
MappingsRegistry.Configure();
Наконец, мои контроллеры имеют ссылку на механизм отображения:
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");
}
}
Теперь все, что осталось, - это указать вашей DI-структуре передать свойство Mapper.Engine
конструктору, а в ваших модульных тестах, очевидно, заменить их соответствующим макетом.