ASP.NET MVC - Частично обновление модели из вида - PullRequest
15 голосов
/ 26 ноября 2009

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

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

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

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

  • Что-то еще?

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

Спасибо.

Ответы [ 6 ]

11 голосов
/ 02 декабря 2009

Это звучит как идеальный сценарий для автопроизводителя. Вы создаете класс модели представления, который содержит подмножество полей или вашей реальной модели, и вы позволяете 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 нет, оно будет сброшено. Поэтому, когда я создаю карту, я указываю ей игнорировать эти недостающие свойства. Я не эксперт 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);

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

5 голосов
/ 21 ноября 2012
  1. Совместите модель просмотра с моделью вашего домена.

  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 = "" });
}
2 голосов
/ 03 декабря 2009

Почему бы вам не использовать 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();
}

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

1 голос
/ 05 декабря 2009

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

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

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

Create([Bind(Exclude = "Entity.EntityId")] ViewModel model) и Edit(ViewModel model) - это методы контроллера Http Post для Create и Edit. Мое представление редактирования имеет скрытое поле ввода для 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, поэтому я определенно использую ее только по мере необходимости. Если у вас есть сложные представления и данные модели в большинстве случаев, вы, вероятно, можете разбить их на более мелкие представления.

1 голос
/ 26 ноября 2009

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

0 голосов
/ 04 декабря 2009

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

Model -> ViewModel -> Model

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

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

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

Пока что я не пользуюсь автоммаперами, но обязательно попробую.

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