Лучшие рекомендации по 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 я делаю следующее, чтобы построить мою модель ViewData​​p >

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;
}
}