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, для почти всего остального мы используем непосредственно модель.
Я пока не использую автоматы, но определенно попробую.