Лучшие рекомендации по ViewModel
Из этого вопроса, похоже, имеет смысл создать контроллер ViewModel, который более точно отражает модель, которую вид пытается показать, но мне любопытно некоторые из условностей (я новичок в шаблоне MVC, если он еще не был очевиден).
В основном у меня были следующие вопросы:
- Мне обычно нравится один класс/файл. Это имеет смысл с помощью ViewModel, если он создается только для передачи данных с контроллера на представление?
- Если ViewModel принадлежит в своем собственном файле, и вы используете структуру каталога/проекта, чтобы держать вещи в отдельности, где принадлежит файл ViewModel. В каталоге Контроллеры?
Это в основном это на данный момент. У меня может возникнуть еще несколько вопросов, но это беспокоило меня в течение последнего часа или около того, и я могу найти последовательное руководство в другом месте.
EDIT:
Глядя на образец приложение NerdDinner на CodePlex, похоже, что ViewModels являются частью Контроллеры, но мне все еще становится неудобно, что они не находятся в их собственных файлах.
Ответы
Ответ 1
Я создаю то, что я называю "ViewModel" для каждого представления. Я помещал их в папку под названием ViewModels в моем веб-проекте MVC. Я называю их после контроллера и действия (или представления), которые они представляют. Поэтому, если мне нужно передать данные в представление SignUp в контроллере Membership, я создаю класс MembershipSignUpViewModel.cs и помещаю его в папку ViewModels.
Затем я добавляю необходимые свойства и методы, чтобы облегчить передачу данных с контроллера на представление. Я использую Automapper, чтобы перейти с моей модели ViewModel в модель домена и обратно, если это необходимо.
Это также хорошо работает для составных ViewModels, которые содержат свойства, относящиеся к типу других ViewModels. Например, если у вас есть 5 виджетов на индексной странице в контроллере членства, и вы создали ViewModel для каждого частичного представления - как вы передаете данные из действия индекса в частичные? Вы добавляете свойство в MemberhipIndexViewModel типа MyPartialViewModel и при рендеринге части, которую вы передадите в Model.MyPartialViewModel.
Выполнение этого способа позволяет вам отрегулировать частичные свойства ViewModel, не изменяя вообще представление индекса. Он все еще просто проходит в Model.MyPartialViewModel, поэтому есть меньше шансов, что вам придется пройти через целую цепочку партитур, чтобы исправить что-то, когда все, что вы делаете, добавляет свойство к частичной ViewModel.
Я также добавлю пространство имен "MyProject.Web.ViewModels" в web.config, чтобы позволить мне ссылаться на них в любом представлении, не добавляя при этом явного оператора импорта в каждом представлении. Просто делает его немного чище.
Ответ 2
Разделение классов по категориям (контроллеры, ViewModels, фильтры и т.д.) нонсенс.
Если вы хотите написать код для раздела Home вашего веб-сайта (/), создайте папку с именем Home и поместите туда HomeController, IndexViewModel, AboutViewModel и т.д. и все связанные классы, используемые действиями Home.
Если у вас есть общие классы, такие как ApplicationController, вы можете поместить его в корень вашего проекта.
Зачем разделять связанные вещи (HomeController, IndexViewModel) и сохранять вещи вместе, которые не имеют никакого отношения вообще (HomeController, AccountController)?
Я написал сообщение в блоге об этой теме.
Ответ 3
Я сохраняю свои классы приложений в подпапке под названием "Core" (или отдельную библиотеку классов) и использую те же методы, что и KIGG пример приложения, но с некоторыми небольшими изменениями, чтобы сделать мои приложения более сухими.
Я создаю класс BaseViewData в /Core/ViewData/, где храню общие свойства сайта.
После этого я также создаю все мои классы ViewData View в той же папке, которые затем выводятся из BaseViewData и имеют определенные свойства вида.
Затем я создаю ApplicationController, из которого выводятся все мои контроллеры. У ApplicationController есть общий метод GetViewData следующим образом:
protected T GetViewData<T>() where T : BaseViewData, new()
{
var viewData = new T
{
Property1 = "value1",
Property2 = this.Method() // in the ApplicationController
};
return viewData;
}
Наконец, в моем действии Controller я делаю следующее, чтобы построить мою модель ViewDatap >
public ActionResult Index(int? id)
{
var viewData = this.GetViewData<PageViewData>();
viewData.Page = this.DataContext.getPage(id); // ApplicationController
ViewData.Model = viewData;
return View();
}
Я думаю, что это работает очень хорошо, и это держит ваши взгляды аккуратными, а ваши контроллеры - тощими.
Ответ 4
Класс ViewModel предназначен для инкапсуляции нескольких частей данных, представленных экземплярами классов, в один простой в управлении объект, который вы можете передать в свой вид.
Было бы разумно иметь ваши классы ViewModel в своих собственных файлах в собственном каталоге. В моих проектах у меня есть подпапка в папке "Модели" с названием "ViewModels". Это где мои ViewModels (например, ProductViewModel.cs
) живут.
Ответ 5
Нет хорошего места для хранения ваших моделей. Вы можете сохранить их в отдельной сборке, если проект большой, и есть много ViewModels (объекты передачи данных). Также вы можете сохранить их в отдельной папке проекта сайта. Например, в Oxite они помещаются в проект Oxite, который также содержит много разных классов. Контроллеры в Oxite перемещаются в отдельный проект, а просмотры также находятся в отдельном проекте.
В CodeCampServer ViewModels названы * Form, и они помещаются в проект пользовательского интерфейса в папке Models.
В проекте MvcPress они помещаются в проект Data, который также содержит весь код для работы с базой данных и немного больше (но я не сделал рекомендую этот подход, он просто для образца)
Таким образом, вы можете видеть, что есть много точек зрения. Обычно я сохраняю свои объекты ViewModels (объекты DTO) в проекте сайта. Но когда у меня есть более 10 моделей, я предпочитаю перемещать их в отдельную сборку. Обычно в этом случае я перемещаю контроллеры для разделения сборки тоже.
Другой вопрос - как легко сопоставить все данные из модели с ViewModel. Предлагаю ознакомиться с библиотекой AutoMapper. Мне это очень нравится, для меня все грязные работы.
И я также предлагаю посмотреть проект SharpArchitecture. Он обеспечивает очень хорошую архитектуру для проектов и содержит множество классных рамок и руководств и отличную коллекцию.
Ответ 6
здесь фрагмент кода из моих лучших практик:
public class UserController : Controller
{
private readonly IUserService userService;
private readonly IBuilder<User, UserCreateInput> createBuilder;
private readonly IBuilder<User, UserEditInput> editBuilder;
public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
{
this.userService = userService;
this.editBuilder = editBuilder;
this.createBuilder = createBuilder;
}
public ActionResult Index(int? page)
{
return View(userService.GetPage(page ?? 1, 5));
}
public ActionResult Create()
{
return View(createBuilder.BuildInput(new User()));
}
[HttpPost]
public ActionResult Create(UserCreateInput input)
{
if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");
if (!ModelState.IsValid)
return View(createBuilder.RebuildInput(input));
userService.Create(createBuilder.BuilEntity(input));
return RedirectToAction("Index");
}
public ActionResult Edit(long id)
{
return View(editBuilder.BuildInput(userService.GetFull(id)));
}
[HttpPost]
public ActionResult Edit(UserEditInput input)
{
if (!ModelState.IsValid)
return View(editBuilder.RebuildInput(input));
userService.Save(editBuilder.BuilEntity(input));
return RedirectToAction("Index");
}
}
Ответ 7
Мы бросаем все наши модели ViewModels в папку Models (вся наша бизнес-логика находится в отдельном проекте ServiceLayer)
Ответ 8
Лично я бы предложил, если ViewModel ничего, кроме тривиального, затем используйте отдельный класс.
Если у вас несколько моделей представлений, я предлагаю разделить его по крайней мере в директории. если модель представления позже разделяется, тогда пространство имен, указанное в каталоге, облегчает переход к новой сборке.
Ответ 9
В нашем случае у нас есть Модели вместе с Контроллерами в проекте отдельно от Представлений.
Как правило, мы пытались перемещаться и избегать большинства объектов ViewData [ "..." ] в ViewModel, поэтому мы избегаем отливок и магических строк, что хорошо.
В ViewModel также содержатся некоторые общие свойства, такие как информация разбивки на страницы для списков или информации заголовка страницы, чтобы рисовать сухари и заголовки. На данный момент базовый класс содержит слишком много информации, на мой взгляд, и мы можем разделить его на три части, самую основную и необходимую информацию для 99% страниц на базовой модели представления, а затем модель для списков и модели для форм, которые содержат конкретные данные для этих сценариев и наследуют от базового.
Наконец, мы реализуем модель представления для каждого объекта для обработки конкретной информации.
Ответ 10
в контроллере:
[HttpGet]
public ActionResult EntryEdit(int? entryId)
{
ViewData["BodyClass"] = "page-entryEdit";
EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
return View(viewMode);
}
[HttpPost]
public ActionResult EntryEdit(Entry entry)
{
ViewData["BodyClass"] = "page-entryEdit";
#region save
if (ModelState.IsValid)
{
if (EntryManager.Update(entry) == 1)
{
return RedirectToAction("EntryEditSuccess", "Dictionary");
}
else
{
return RedirectToAction("EntryEditFailed", "Dictionary");
}
}
else
{
EntryEditViewModel viewModel = new EntryEditViewModel(entry);
return View(viewModel);
}
#endregion
}
код в поле зрения:
public class EntryEditViewModel
{
#region Private Variables for Properties
private Entry _entry = new Entry();
private StatusList _statusList = new StatusList();
#endregion
#region Public Properties
public Entry Entry
{
get { return _entry; }
set { _entry = value; }
}
public StatusList StatusList
{
get { return _statusList; }
}
#endregion
#region constructor(s)
/// <summary>
/// for Get action
/// </summary>
/// <param name="entryId"></param>
public EntryEditViewModel(int? entryId)
{
this.Entry = EntryManager.GetDetail(entryId.Value);
}
/// <summary>
/// for Post action
/// </summary>
/// <param name="entry"></param>
public EntryEditViewModel(Entry entry)
{
this.Entry = entry;
}
#endregion
}
проекты:
-
DevJet.Web(веб-сайт ASP.NET MVC
проект)
-
DevJet.Web.App.Dictionary(a
отдельный проект библиотеки классов)
в этом проекте я сделал несколько папок, например:
DAL,
BLL,
BO,
VM (папка для моделей просмотра)
Ответ 11
Создайте базовый класс модели представления, который имеет обычно требуемые свойства, такие как результат операции и контекстные данные, вы также можете поместить текущие пользовательские данные и роли
class ViewModelBase
{
public bool HasError {get;set;}
public string ErrorMessage {get;set;}
public List<string> UserRoles{get;set;}
}
В базовом классе контроллера есть такой метод, как PopulateViewModelBase(), этот метод будет заполнять контекстные данные и пользовательские роли.
HasError и ErrorMessage, устанавливают эти свойства, если есть исключение, вытягивая данные из службы /db. Привяжите эти свойства к виду, чтобы показать ошибку.
Роли пользователя могут использоваться для отображения скрытой секции в представлении на основе ролей.
Чтобы заполнить модели представления в разных действиях get, это можно сделать согласованным с помощью базового контроллера с абстрактным методом FillModel
class BaseController :BaseController
{
public PopulateViewModelBase(ViewModelBase model)
{
//fill up common data.
}
abstract ViewModelBase FillModel();
}
В контроллерах
class MyController :Controller
{
public ActionResult Index()
{
return View(FillModel());
}
ViewModelBase FillModel()
{
ViewModelBase model=;
string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString();
try
{
switch(currentAction)
{
case "Index":
model= GetCustomerData();
break;
// fill model logic for other actions
}
}
catch(Exception ex)
{
model.HasError=true;
model.ErrorMessage=ex.Message;
}
//fill common properties
base.PopulateViewModelBase(model);
return model;
}
}