Подтвердить что ты не робот

Лучшие рекомендации по ViewModel

Из этого вопроса, похоже, имеет смысл создать контроллер ViewModel, который более точно отражает модель, которую вид пытается показать, но мне любопытно некоторые из условностей (я новичок в шаблоне MVC, если он еще не был очевиден).

В основном у меня были следующие вопросы:

  • Мне обычно нравится один класс/файл. Это имеет смысл с помощью ViewModel, если он создается только для передачи данных с контроллера на представление?
  • Если ViewModel принадлежит в своем собственном файле, и вы используете структуру каталога/проекта, чтобы держать вещи в отдельности, где принадлежит файл ViewModel. В каталоге Контроллеры?

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

EDIT: Глядя на образец приложение NerdDinner на CodePlex, похоже, что ViewModels являются частью Контроллеры, но мне все еще становится неудобно, что они не находятся в их собственных файлах.

4b9b3361

Ответ 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;
}
}