Действия по обновлению AutoMapper в ASP.NET MVC - PullRequest
2 голосов
/ 25 мая 2011

Возможно, для некоторых это довольно просто, но я немного растерялся и не могу найти достойного примера.Скажем, я использую модели представления, и мое действие POST использует эту модель представления.Обычно я делаю что-то вроде следующего:

    [HttpPost]
    public ActionResult Update(UserViewModel uvm)
    {
        User user = Mapper.Map<UserViewModel, User>(uvm);
        _repository.Update(user);

        return RedirectToAction("Index");
    }

Хотя это не полная картина.Сопоставление будет работать нормально, однако, если бы я просто обновил то, что я сопоставил, тогда бы он избавился от ценных данных в базе данных, потому что, конечно, в этом случае я не обновляю пароль или другие детали.

Мой репозиторий выглядит примерно так:

    public void Update(User user)
    {
        User u = Session.QueryOver<User>().Where(x => x.UserName == user.UserName).SingleOrDefault();

        if (u == null)
            throw new Exception("User not found");

        u.Forename = user.Forename;
        u.Surname = user.Surname;
        u.EmailAddress = user.EmailAddress;
    }

[Я использую NHibernate, поэтому он сохранит объект обратно в БД после закрытия сессии (после завершения запроса).) автоматически для меня.]

Поэтому мой вопрос заключается в том, чтобы в моем хранилище я должен был загрузить сущность «Пользователь», затем обновить нужные значения и затем сохранить их обратно, или же существует другой методсделать это?Причина, по которой я спрашиваю, заключается в том, что кажется немного ... "ручным", если вы понимаете, о чем я?Возможно, это правильно, но я просто хотел узнать мнение тех, у кого больше опыта в этой области.

Приветствия

1 Ответ

8 голосов
/ 25 мая 2011

Я использую следующий подход:

[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 конструктору, а в ваших модульных тестах, очевидно, заменить их соответствующим макетом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...