ASP.NET MVC - частичное обновление модели из представления

Я просто задавался вопросом, как люди приближаются к этой ситуации. Это то, что кажется слабым моментом в моем использовании MVC с ORM (NHibernate в этом случае)...

Скажите, что у вас в вашей модели мелкозернистая и сложная сущность. Вероятно, у вас будет страница администратора для управления объектами этого типа. Если объект сложный, маловероятно, что вы будете модифицировать весь объект в одной форме. Вам все равно необходимо передать соответствующие свойства в представление и включить изменения в эти свойства в модели, когда представление вернет их.

Что делает кто-нибудь в этой ситуации?

  • Создайте модель представления, которая (или содержит) подмножество свойств сущностей. Передайте это и из представления. В методе действий "edit" в контроллере, получите объект из репозитория, перейдите по всем свойствам в ViewModel и примените их к объекту Model (model.a = viewmodel.a, modelb = viewmodel.b). Это кажется очевидным разумным путем, но генерирует много утомительного кода сантехники. Также это немного усложняет проверку.

  • Что-то еще?

Я кратко посмотрел на automapper - но это, похоже, не соответствует действительности, может быть, я ошибаюсь?

Спасибо.

Ответы

Ответ 1

Это звучит как идеальный сценарий для automapper. Вы создаете класс модели представления, который содержит подмножество полей или вашей реальной модели, и вы позволяете AutoMapper заботиться о выдаче значений из объекта модели домена в ваш объект модели просмотра. Какие проблемы вы имеете с этим подходом?

Рассмотрим следующий пример:

Вот ваша модель домена и модель вашего представления

public class Person
{
    public string FirstName
    { get; set; }

    public string LastName
    { get; set; }

    public string HomeNumber
    { get; set; }

    public string Address1
    { get; set; }

    public string Address2
    { get; set; }
}

public class PersonViewModel
{
    public string FirstName
    { get; set; }

    public string LastName
    { get; set; }

    public string HomeNumber
    { get; set; }
}

Вот ваше отображение, вам нужно создать сопоставление в обоих направлениях от dm- > vm и vm- > dm.

Из того, что я видел при использовании Automapper, является то, что если вы сопоставляете объекты A с B, а B имеет свойство, которое A не имеет, оно будет reset. Поэтому, когда я создаю карту, я направляю ее, чтобы игнорировать те недостающие свойства. Я не эксперт Automapper, поэтому я могу использовать его неправильно.

Mapping

Mapper.CreateMap<Person, PersonViewModel>();
// Automapper will reset values in dest which don't exist in source, so be sure to ignore them!
Mapper.CreateMap<PersonViewModel, Person>()
    .ForMember(dest => dest.HomeNumber, opt => opt.Ignore());

Наконец, использование:

Person p = new Person()
{
    FirstName = "First",
    LastName = "Last",
    Address1 = "add 1",
    Address2 = "add 2"
};

PersonViewModel pvm = Mapper.Map<Person, PersonViewModel>(p);
// Map to a new person
Person p2 = Mapper.Map<PersonViewModel, Person>(pvm);

// Map to the existing person just to update it
Person p3 = new Person()
{
    HomeNumber = "numberHere"
};

// This will update p3
Mapper.Map<PersonViewModel, Person>(pvm, p3);

Из-за исключения, это, очевидно, меньше идеального, но намного лучше, чем ручное выполнение всего этого.

Ответ 2

  • Представьте, что ваша модель обзора сопоставлена ​​с вашей моделью домена один к одному.

  • Укажите Model в качестве аргумента для routeValues, как показано ниже. Это означает, что ваша модель представления будет инициализирована значениями из модели домена. Только результирующий набор полей в форме будет перезаписан в результирующем personViewData.

Обновить вид:

@model ViewModel.PersonView

@using (Html.BeginForm("Update", "Profile", Model, FormMethod.Post))
{
  ...Put your sub set of the PersonView fields here
}

ProfileController:

public ActionResult Update(string userName)
{
    Person person = _unitOfWork.Person.Get().Where(p => p.UserName == userName).FirstOrDefault();
    PersonView personView = new PersonView();
    Mapper.Map(person, personView);

    return View(personView);
}

[HttpPost]
public ActionResult Update(PersonView personViewData)
{
   Person person = _unitOfWork.Person.Get().Where(p => p.UserName == personViewData.UserName).FirstOrDefault();
   Mapper.Map(personViewData, person);
   _unitOfWork.Person.Update(person);
   _unitOfWork.Save();

   return Json(new { saved = true, status = "" });
}

Ответ 3

Почему вы не используете TryUpdateModel с коллекцией форм.

Если ваш просмотр редактирует человека

public class Person
{
    public string ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Address { get; set; }
}

И ваше представление только редактирует имя и фамилию, вы можете сделать это:

public ActionResult Action(FormCollection form)
{
    Person personToUpdate = Repository.GetPerson(form["ID"]);

    TryUpdateModel<Person>(personToUpdate, form);

    Repository.Update(personToUpdate)

    return View();
}

Это будет только обновлять Person с элементами, которые являются частью коллекции форм. Если вы не хотите, чтобы поле обновлялось, не отправляйте его с формой.

Ответ 4

Что делать, если у вас есть полная модель, но каждая страница использует и обновляет только требуемую часть? Затем вы обновляете бизнес-модель, используя полные данные просмотра на последней странице.

Ответ 5

y, viewmodel + automapper.

пс. сложная модель, имеет тенденцию... усложнять все - действительно ли это стоит в вашем сценарии?

Ответ 6

Я использую подобный подход к вашему (в моем случае Entity Framework) с Entity → ViewModel → View, но только для представлений с "сложными" объектами, которые имеют отношения 1: M или M: M. В большинстве случаев я пошел по низкой дороге и пошел на Entity- > View, когда у меня есть простой объект.

My ViewModel определяется как поддерживающие свойства Entity +: SelectList или MultiSelectList и либо string, либо List<string>. Я также буду использовать ViewModel для экземпляров, где у меня есть свойства, необходимые для представления, но необязательно в сущности (базе данных).

Http Получить методы контроллера - это простые ActionResults с return View(repository.FetchNewViewModel()) для Create или repository.FetchModelById(id) для Edit. В обоих случаях я инициализирую свои объекты перед передачей их в представление.

Create([Bind(Exclude = "Entity.EntityId")] ViewModel model) и Edit(ViewModel model) - это методы контроллера Http Post для создания и редактирования. В моем окне редактирования есть скрытое поле ввода для EntityId, чтобы передавать его назад и вперед.

К тому моменту, когда метод Http Post имеет режим просмотра, я теряю все значения Entity.Relation и ViewModel. (Multi) SelectList. Я должен перестроить объект, если я хочу, чтобы мое представление отображалось правильно:   `

try
{
    var tags = model.TagIds; // List<string> or <int> depending on your Id type
    if (model.TagsList == null) // It will be
    {
        model.TagsList = _repository.FetchSelectedTags(tags); // Build a new SelectList
    }

    if (!ModelState.IsValid)
    {
        return View(model);
    }

    _repository.Add(model.Article, tags); // or Save() for Edit
}
catch
{
    return View(model); // Generally means something screwed in the repository class
}

return RedirectToAction("Index");

`

Возможно, 30% моей сущности основывается на ViewModel, поэтому я определенно использую ее только по мере необходимости. Если у вас есть сложные представления и данные модели в большинстве случаев, вы, вероятно, можете разбить их на более мелкие.

Ответ 7

Сейчас я работаю над большим проектом, используя S # arp Architecture и im, также используя подход:

Model -> ViewModel -> Model

Я использую ViewModel для части Binding и Validations, другой подход - использовать модель Directly (с помощью tryUpdateModel/UpdateModel, которую мы использовали во время разработки прототипа), но для сложных сценариев мы в конечном итоге обрабатываем особую ситуацию, например SelectLists/Checkbox/Projections/HMAC Validations в маленькой ViewModel в любом случае и с использованием большого количества Request.Form [ "key" ] = (, другой недостаток обрабатывает ошибки, в которых вы хотите повторно заполнить форму с помощью ввода пользователем, я нашел ее немного сложнее с использованием модели напрямую (с помощью ViewModel мы берем много преимуществ от попыток ModelState, сохраняя нам пару поездок в БД, любой, кто столкнулся с этим сценарием, будет знать, что я имею в виду).

Этот подход требует много времени, так же, как вы сказали, вы в конечном итоге отвечаете свойствам, но, на мой взгляд, это путь для сложных форм.

Стоит упомянуть, что мы просто используем ViewModels для сценариев Create/Edit, для почти всего остального мы используем непосредственно модель.

Я пока не использую автоматы, но определенно попробую.