Организуем view models в ASP.NET MVC



    В интернете полно примеров вроде «Делаем вики на ASP.NET MVC за 15 минут». Проблема таких примеров в том, что они используют VideData или ViewBag для передачи данных в View. Для генерации форм используются нетипизированные методы вроде Html.TextBox(). А для получения данных из форм просто параметры к методам контроллера, или хуже того – сами сущности из ORM.

    Это может быть прекрасно с точки зрения того, кто в жизни имеет дело только с созданием таких «видео уроков». Но в немного более сложных случаях вы, конечно же, захотите иметь строго типизированные модели, использовать строго типизированные методы вроде Html.TextBoxFor(m=>..), и получать в методе контроллера из формы ровно то, что хотите получить и при этом держать все модели в консистентном виде.

    Итак, правила, если вы хотите разрабатывать в MVC:

    • Каждому View свой персональный класс ViewModel.
    • Только View диктует, какие будут свойства у ViewModel. Остальное – проблемы контроллера.
    • ViewModel – это простой DTO, без логики.
    • View использует только те данные, которые приходят из ViewModel, ничего больше. Не трогайте Request.IsAuthenticated в ваших View, для этого есть модель.

    Может возникнуть вопрос, а как быть с _Layout (мастер страницами) – ведь это тоже View и они тоже хотят свои модели. Вполне резонно – ведь мы помним, что view диктует модель, всё остальное следует потом. Поэтому создайте в приложении класс SharedLayoutViewModel с нужными вашему _Layout.cshtml свойствами, и наследуйте от него ваши модели для остальных View. Пользуйтесь Result-фильтрами чтобы наполнять эту модель в одном месте для всех методов ваших контроллеров. Или переопределите OnResultExecuting в базовом классе ваших контроллеров, и делайте это там.

    Для удобства организуйте ваши модели в директории. Вполне жизнеспособным выглядит вариант такой структуры:


    Обрабатываем формы


    Попробуем создать контроллер, модель и view для такой формы:


    Для этого мы добавим View, в качестве модели которого укажем AccountRegisterViewModel:
    @model AccountRegisterViewModel
    <h2>
      Register</h2>
    @using (Html.BeginForm())
    {
      @Html.ValidationSummary()
      <label>
        Name
        @Html.TextBoxFor(m => m.Form.Name)
      </label>
      <label>
        State
        @Html.DropDownListFor(m => m.Form.State, Model.StateSelectList)
      </label>
      <input type="submit" value="Register me" />
    }

    Добавим в проект в директорию ViewModels/Account класс модели, который включает в себя данные для выпадающего списка State и свойство с самой формой (здесь одна, а в жизни их может быть несколько в одном View):
    public class AccountRegisterViewModel : SharedLayoutViewModel<br/>
    {<br/>
        public AccountRegisterForm Form { get; set; }<br/>
     <br/>
        public IEnumerable<SelectListItem> StateSelectList { get; set; }<br/>
    }

    Для нашей формы мы определим отдельный класс. Он будет включать себя нужные атрибуты DataAnnotations для валидации формы, и может быть, даже когда-нибудь реализует IValidatableObject:
    public class AccountRegisterForm<br/>
    {<br/>
        [Required]<br/>
        public string Name { get; set; }<br/>
     <br/>
        [Required]<br/>
        public string State { get; set; }<br/>
    }

    Сам контроллер тога будет выглядеть так:
    [HttpGet]<br/>
    public ActionResult Register()<br/>
    {<br/>
        var model = new AccountRegisterViewModel();<br/>
     <br/>
        SetupRegisterViewModel(model);<br/>
        return View(model);<br/>
    }<br/>
     <br/>
    [HttpPost]<br/>
    public ActionResult Register([Bind(Include = "Form")]AccountRegisterViewModel model)<br/>
    {<br/>
        if (ModelState.IsValid)<br/>
        {<br/>
            // save model.Form <br/>
     <br/>
            TempData["Message"] = "Thank you for registering";<br/>
            return RedirectToAction("Index");<br/>
        }<br/>
     <br/>
        SetupRegisterViewModel(model);<br/>
        return View(model);<br/>
    }<br/>
     <br/>
    private void SetupRegisterViewModel(AccountRegisterViewModel model)<br/>
    {<br/>
        model.StateSelectList = new SelectList(new[] { string.Empty"NY""VA" });<br/>
    }


    Вот и всё.

    Надеюсь, этот пример станет полезным разработчикам ASP.NET MVC, особенно тем, кто создаёт на нём приложения, а не видео уроки.

    P.S. Отмечу, что выбранный пример для этого подхода не показательный. Форма из двух полей без форматирования выбрана только ради экономии времени читателя.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 60

      +1
      Несколько сложноваты имена для ViewModels. Достаточно было бы назвать так же (файлы), как и View. Для данного примера приведеный код, как мне кажется, избыточен. Неудачный Вы выбрали пример.

      А последним хочется отметить: т.к. вы вначале написали про использование всеми подряж ViewData & ViewBag, то это Вас вынудило в конце использвать TempData, правильно? :)
        +1
        Хотя, в случае с TempData я не прав, пожалуй :)
          0
          Отчасти правы, для случая с TempData[«Message»] я обычно использую обёртку из базового класса контроллера: SetTempMessage(..), а в самом базовом классе модели есть свойство TempMessage, которое используется в Layout, чтобы показать сообщение.

          Но опять же, это история другого романа.
          0
          В контексте того, что все модели находтся в одном пространстве имён (NameSpaceProvider=False для их директорий), то имена различаются до основания. Можно убрать ViewModel и оставить Model — на вкус, суть не меняется.

          Конечно для такой формы подход избыточен, но в жизни мало встречается формы из двух полей. О них и речь. Не буду же я здесь размещать килобайты разметки для повышения наглядности примера.
          0
          Но ведь если формы на сайте как правило одинаковы по стилю и html коду, почему бы не строить всю форму целиком из классов ViewModel, вместо расстановки однотипных Html.Label Html.TextBox. Хотелось бы увидеть в будущем статью об этом :-)
            0
            Унификация всех форм — это конечно утопия.
            Но создание Html.InputFor(m=>m.Field), где сам label, разные звёздочки (обязательное поле) и инпут строится целиком по метаданным модели — это вполне практичный подход.

            О плюсах/минусах такого подхода у себя в блоге как-то писал Jimmy Bogard. Впрочем, там много интересных заметок на тему MVC есть, среди прочего: How we do MVC
              0
              Если формы действительно стандартизированы, и для особых исключений, в виде аннотаций к ViewModel подписаны нужные UIHint-ы, и созданы EditorTemplates, то вполне. Это можно делать уже сейчас.
              0
              +1. Очень хороша книжка Стивена Сандерсона “Pro ASP.NET MVC 2 Framework”. Благодаря тому, что я начал знакомство с MVC именно с неё, мне удалось избежать многих ошибок, часть из которых вы здесь упомянули. После 4 месяцев «пост-релизной» поддержки проекта я ни разу не жалею, что сразу сделал всё по-уму :)
                0
                Не трогайте Request.IsAuthenticated в ваших View, для этого есть модель.

                Покажите, пожалуйста, как вы предлагаете бороться с использованием IsAuthenticated во вьюшках, кроме банального пробрасывания этого поля и такого же использования как и то, с чем боремся.
                  +1
                  Не делайте View слишком умными. Избегайте глобальных контекстов.
                  Я не настолько ортодоксален, как предлагают некоторые, чтобы избегать в них @if.
                  Но с точки зрения View, там где вы используете IsAuthtenticated, скорее всего должно быть свойства вроде [bool]Model.ShowUserControls и другие — смотрите на это со стороны View.

                  Однажды, вы захотите использовать этот же шаблон к примеру генерирующий ту же страницу в режиме для администратора: View this page from USER1 perspective. И вуаля — все ваши шаблоны это поддерживают. Это только 1 конкретный пример почему данные должны быть явными, а шаблоны глупыми.

                  Именно контроллер должен позаботиться о том, чтобы View была предоставленна необходимая (не больше) для него модель собранная из данных, контекста и т.п.

                    0
                    Я вполне понимаю что шаблоны должны быть глупыми и всю концепцию MVC. Но всегда есть мелкие проблемы с которыми неудобно бороться через похожее выпиливание лобзиком по вазелину.

                    Простой пример — надо показать залогиненому пользователю один текст, незалогиненому другой. Верстка — разная ( т.е. передать текст из контроллера не получится, передавать с версткой — глупости). Пилить 2 контрола на каждую простую вьюшку — ну тоже странновато.
                    Максимум что стоит делать для изолированности — пробрасывать значение через модель.

                    Проблемы с «однажды я захочу...» я бы предпочел отложить на то время, когда захочу — пока такой вопрос не стоит — я не решаю его (преждевременная оптимизация… ). К тому же в вашем примере проблема решается имперсонацией, а не запретом Request.IsAuthenticated.

                    Ну т.е. я искренне хотел бы увидеть практический пример как надо решать эту проблему и обоснование — почему надо решать именно так. А то получается догматизм какой-то — «нельзя» — «почему нельзя» — «нарушаешь атсральные заповеди»… Если у вас есть решение — приведите пожалуйста.
                      +1
                      Всега стараюсь выступать с практических позиций.

                      Если вам в конкретном View удобно пробросить IsAuthenticated в модель — делайте это.
                      Если в конексте использования, вы понимаете, что IsAuthenticated не уместно, а должно быть (со стороны View) ShowUserControls — то пробрасывайте IsAuthenticated в виде ShowUserControls.
                      Только не заставляйте View _знать_ что IsAuthenticated всегда равно ShowUserControls. Потому что тогда вам самому придётся это тоже запомнить, а такие мелочи копятся.

                      Не надо ударсять в крайности — надо искать рациональное зерно, и применять только если понимаешь зачем.

                      И это не преждевременная оптимизация, это Prefactoring =)
                        0
                        «Только не заставляйте View _знать_ что IsAuthenticated всегда равно ShowUserControls. Потому что тогда вам самому придётся это тоже запомнить, а такие мелочи копятся.»

                        Это точно! Мы на практике рассматриваем эту вещь как часть AOP и используем для этого ViewBag, атрибуты контроллера, которые добавляют это во ViewBag и ViewHelper-ы для непосредственного использования.

                        Разумеется, если IsAuthenticated имеет _прямое и непосредственное_ отношение к текущему View — оно находится во View-модели (поскольку, больше не является аспектом, а является частью конкретной модели, которая отображается).
                        0
                        «Верстка — разная ( т.е. передать текст из контроллера не получится, передавать с версткой — глупости). Пилить 2 контрола на каждую простую вьюшку — ну тоже странновато.»

                        Можете чуть-чуть уточнить пример?
                          0
                          Для неаутентифицированного пользователя — описание общих сценариев использования
                          Для аутентифицированного — подробное описание каждого сценария с несколькими quick-start кнопками\ссылками.

                          Как-то так.
                            0
                            Я в таких случаях пользуюсь RenderAction — в соответствующем контроллере есть логика, которая проверяет IsAuth (значение берется из входного параметра с использованием соответствующего Binder-а), а затем контроллер выдает один из двух контролов. И ничего, что они простые, зато следуют SRP. Простота во вью еще важнее, чем в исполняемом коде.
                      +1
                      Первое правило — View не должно зависеть от Request, как кстати, и контроллер.

                      Вы можете включить свойство IsUserAuthenticated в исходящую View-модель (для этого зачастую приходится создавать layer-supertype, то есть базовую ViewModelBase), или же использовать для этого ViewBag (который весьма разумно использовать для таких «побочных» вещей, но не рекомендуется для передачи основных данных — для этого есть View-модель).
                      0
                      А зачем прямо уж на каждую страницу создавать ViewModel? Я правда не вижу выгоды. У вас же есть домен, в нем есть User, которого с тем же успехом можно использовать во View. И в нем же можно прописать правила валидации. Особенно если используется CodeFirst модель.

                      То есть я хочу сказать, что упор надо делать на домен, а не на ViewModel, тогда и работы меньше придется делать и модель будет очевидней.
                        0
                        Первоначально да. Но сущетсвенная проблема заключается в том, что рано или поздно правила валидации выходят за рамки сущностей модели, иногда противоречит им, и вы начинаете размазывать валидацию по проекту, часто дублировать её, делать исключения то там то там.

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

                        Кстати, самый удачный пример реализации валидации — на мой взгляд реализовано в Python FormEncode. Где валидатор — это объект со своей гибкой структурой, произвольной вложенность, и заодно ModelBinder (в понятиях MVC).
                          0
                          А как это делает FormEncode в Python?

                          Насчет ASP.NET MVC — мне очень НЕ нравится, что там на архитектурном уровне смешали валидацию и Model-binding, и то, что DefaultModelBinder всегда проводит валидацию, даже когда это не нужно. Но это пол-беды — ведь есть еще domain-level-валидация (построенная, например, на FluentValidation), и приходится серьезно поднапрячься, чтобы поддерживать ModelState в нормальном состоянии (чтобы не выводить лишних ошибок, и чтобы при этом были выведены все ошибки формы за раз).

                          На мой взгляд, это можно было сделать порядком проще, и чтобы не пришлось заморачиваться с ModelValidatorProvider'ами. Нужно было сделать DataAnnotations менее навязчивыми, что ли.
                            0
                            Это немного оффтопик. Но всё равно.

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

                            >>> class Registration(formencode.Schema):
                            ... first_name = validators.String(not_empty=True)
                            ... last_name = validators.String(not_empty=True)
                            ... email = validators.Email(resolve_domain=True)
                            ... username = formencode.All(validators.PlainText(),
                            ... UniqueUsername())
                            ... password = SecurePassword()
                            ... password_confirm = validators.String()
                            ... chained_validators = [validators.FieldsMatch(
                            ... 'password', 'password_confirm')]


                            А дальше атрибутом (можно и в коде) у метода контроллера просто:
                            @validate(schema=Registration())

                            В общем случае создаётся набор базовых валидаторов — для различных случаев, и они комбинируются (учитывая к примеру множетсвенное наследоание в Python). Это даёт огромный плюс в переиспользовании и унификации форм.

                            В практике — очень практиченое решение. Кроме того, позволяет сделать валидацию для _любой_ формы с какой угодно вложенностью и хитростями, не прибегая к сделкам с совестью.

                            formencode.org/Validator.html

                              0
                              Да, и правда здорово. Вот в ASP.NET MVC надо было по-умолчанию сделать валидацию с помощью таких атрибутов, чтобы где надо их и указывать. Насчет гибкости валидатора — в .NET есть тот же FluentValidation (прекрасная вещь), но DefaultModelBinder «из коробки» юзает DataAnnotations.
                          0
                          Это в примерах, о которых автор говорил с самого начала так делается. И поначалу кажется очень простым. А потом, на практике, оказывается, что между Domain Model и Presentation Model более, чем прилично различий. Так что отнюдь не с тем же успехом.

                          Кроме того, поскольку User, как класс модели домена, может быть прикреплен к сессии ORM, это может позволить делать во View то, чего там делать не следует.

                          ViewModel, возможно, придется создавать не на каждую страницу, но на большинство. Самая засада — mapping из доменной модели во ViewModel, и обратно. Но и эта проблема решается с помощью AutoMapper'а.

                          Вообще, считайте ViewModel — это как контракт между контроллером и View.

                          P.S. Валидация — это вообще отдельная большая история. С учетом того, что существует множество видов валидации — структура сильно зависит от конкретной ситуации.
                            0
                            Не расскажете, что такое AutoMapper?
                              0
                              AutoMapper — отличная утилита, позволяющая на базе заранее заданных правил трансформировать одну модель в другую (например, создавать проекцию domain-модели во ViewModel — один из наиболее частых сценариев).

                              Позволяет избежать целой массы рутинного кода в контроллерах или сервисах. Однако, использовать нужно также с умом — каждый раз думать, как, например, все это дело сопрягается с Fetching-стратегиями вашей ORM (особенно, если у вас одна и та же View-модель используется в нескольких View).
                                0
                                Интересно, спасибо.
                              –1
                              Да, я понимаю что домен может разойтись с отображением. И тогда у вас будет 2 варианта: либо делать DTO, либо пересматривать домен. Мне кажется, что лучше изменить домен.
                                +1
                                Ну нет, в корне не согласен. Domain не должен зависеть от View. В этом и смысл домена — это логика, которая не зависит от представления и может быть использована в разных приложениях (например, в MVC-App, в WCF-сервисе, и где-то еще). Как только вы свяжете домен с MVC-представлением, все остальные вещи отпадают.
                                  –1
                                  Разумеется не должен, я этого и не говорил. Я сказал что если вы не можете смаппить домен на вью, то есть 2 варианта решения, пересмотреть домен, или делать костыли в виде ViewModel, которые по-сути раздувают домен со всех сторон.
                                    +2
                                    Ну нифига себе, костыли :) ViewModel — это самый что ни на есть строгий контракт между контроллером и View, совершенно предсказуемый и тестируемый. А вот «пересмотреть домен из за View» — вот это самый-самый костыль.
                                      0
                                      Вы должно быть подумали, что я предлагаю добавлять в домен такие вещи как, например, IsUserLocationVisible, которые служат исключительно для того, чтобы определить показывать местоположение юзера на сайте или нет. Если добавить это поле в домен, то конечно же домен начинает зависить от View. И это будет костылем. Вы бы, я так полагаю, добавили это поле во ViewModel.

                                      Я же предлагаю следующее, формализовать то бизнес-правило, на основании которого определяется показывать юзера на сайте или нет и внести его в домен. Допустим нам нужно показывать местоположение юзера, только если он дружелюбный, тогда в домене это будет поле IsFriendly с некой логикой внутри. Которое никаким образом не связано с UI. И на основании уже этого поля определять во View показывать его или нет.

                                      В итоге, я хочу сказать, что вместо того чтобы добавлять поле IsUserLocationVisible во ViewModel лучше добавить в домен IsFriendly.
                                        0
                                        Это само собой, поскольку это так называемый «domain concern» — это действительно никак не связано с View. Ровно как не связано и с необходимостью наличия/отсутствия ViewModel-ей, поскольку требование того, что пользователю должен быть «Friendly» или «Non-Friendly» продиктовано не UI, а предметной областью, концепцией проекта.

                                        Тут речь скорее о том, что от ViewModel-ей никуда не деться, если domain становится достаточно сложным (а чем он сложнее — тем больше различий между domain- и presentation-моделями).

                                        P.S. В вашем примере, во ViewModel в любом случае не было бы никаких «IsUserLocationVisible» — определить «визибл» она или нет — это логика View, и не должна находиться в контроллере. Во ViewModel было бы то же самое свойство IsFriendly.
                                0
                                Тогда уж EmitMapper, который в несколько раз быстрее
                              –3
                              А теперь вопрос- зачем? :) Зачем на каждый чих создавать ViewModel, даже для простых форм с несколькими полями? А для страниц с простыми сообщениями- тоже создавать? Ок, прикинем- стандартный проект, скажем 30 форм. Получается 30 ViewModel, куча мапперов, куча лишних файлов и т.д. Это отжирает кучу времени при нулевых бонусах. Архитектура ради архитектуры. Код ради кода. На самом деле в жизни нужно использовать комбинированный подход- где это имеет смысл, там использовать Presentation Model, где нет- не использовать. KISS.
                                0
                                P.S. Я в курсе про AutoMapper, но сути это не меняет.
                                  +1
                                  От части я с вами согласна, но вот беда, когда это все тестируешь и рефакторишь, ту пусть будет больше файлов, чем отсутствие типизации.
                                    0
                                    На самом деле, и да, и нет.

                                    Я одно время сам был сторонником исключительно строгих контрактов. Однако, в контексте того же ASP.NET MVC оказалось очень предпочтительно использовать ViewBag для передачи дополнительных данных во View (что-то вроде AOP). С одной стороны — loose-contract, а с другой — он не имеет отношения к основному View, не засоряет его ViewModel, и не мешает всей strong-type-овости всех основных View.

                                    Для основных View, все же, на практике оказалась более предпочтительной модель со строгой типизацией View. Количество ViewModel-ей перестает волновать, когда они созданы по определенному соглашению, лежат в нужном месте и хорошо структурированы.
                                    0
                                    Полностью согласен. Еще обычно в таких проектах на 30 форм получается куча интерфейсов, репозиториев и сервисов, которые в итоге сводятся к простому селекту в базе данных.
                                      +1
                                      когда даже над одной страницей работают больше 2х человек ( даже 2 — уже неудобно) — нестрогая типизация ViewModel приводит к головной боли — лазить и смотреть как же твой коллега назвал то, куда он данные Х сложил. А еще есть опечатки при верстке (верстальщик же не должен быть программистом, он сделал все правильно, это у вас не работает ) и рефакторинг такого — тот еще ад.
                                      В общем я тоже всеми конечностями за строгую типизацию, ну или хотя бы за dynamic который в compile time проверяется…
                                        0
                                        Поясните, пожалуйста, мысль, каким образом dynamic может проверятся в compile time.
                                          0
                                          Никак не проверяется, видимо ошибся товарищ. С dynamic-типами работает DLR (используя позднее связывание).
                                            0
                                            Имелось ввиду использование ExpandoObject как вот тут (ноги растут отсюда)

                                            Честно скажу — сам еще это не пробовал — пока некогда. Но как я понял — это все проверяется в Compile Time, по типу того же Clay part 1,part 2
                                              +1
                                              Нет, нет, все проверяется в runtime (связывание происходит совершенно точно в DLR, с использованием DynamicMetaObject).
                                            0
                                            dynamic — это loose-контракт, и никак не альтернатива и не «хотя бы» по сравнению со строгой типизацией. Но использовать можно, просто для особых сценариев.
                                          +1
                                          Хорошая статья и не менее хорошая к ней дискуссия.
                                          1andy, если не будите писать дальше статьи про MVC, то я, пожалуй, буду являться к вам во сне, чтобы внушить вам это.
                                            0
                                            Мне нравится такой подход (максимально сокращённо, насколько позволяет формат комментариев)

                                            Интерфейс

                                            public interface IAccountRegisterForm
                                            {
                                            string Name { get; set; }
                                            string State { get; set; }
                                            void Validate();
                                            }

                                            Модель

                                            public class AccountRegisterForm: IAccountRegisterForm
                                            {
                                            [Required]
                                            public string Name { get; set; }
                                            [Required]
                                            public string State { get; set; }

                                            public void Validate(){}
                                            }

                                            Вьюшка

                                            @model IAccountRegisterForm

                                            @Html.TextBoxFor(m => m.Name)


                                            Тогда случай, описанный в статье, становится просто частным случаем. А одну и ту же вьюшку можно использовать для отображения общих данных различных моделей — если они наследуют один и тот же интерфейс.

                                            В данном случае примером может быть, регистрация пользователя и администратора, если этот процесс несколько отличается валидацией и набором полей.
                                              0
                                              А как использовать один интерфейс, если поля различаются?
                                                0
                                                На одной из страниц:

                                                @model AdminRegistrationModel

                                                @Html.Partial(«CommonRegisterForm», Model)
                                                @Html.TextBoxFor(m => m.AdminSpecifiField)

                                                На другой
                                                  0
                                                  Случайно отправил неполный комментарий.

                                                  На другой

                                                  @model UserRegistrationModel

                                                  @Html.Partial(«CommonRegisterForm», Model)
                                                  @Html.TextBoxFor(m => m.UserSpecificField)

                                                0
                                                Разумный подход и хороший вариант для таких вот спорных моментов — как сохранить строгий контракт и не привязаться к слишком конкретному типу.
                                                0
                                                Тогда получается, что в одном и том же интерфейсе у вас будет и UserSpecificField и AdminSpecifiField. Что противоречит одному из принципов S.O.L.I.D, а именно Interface segregation principle.
                                                  +1
                                                  Эм, нет. UserSpecificField и AdminSpecifiField — свойства разных моделей.

                                                  public class UserRegistrationModel: IAccountRegisterForm
                                                  {

                                                  String UserSpecificField{get; set;}
                                                  }

                                                  public class AdminRegistrationModel: IAccountRegisterForm
                                                  {

                                                  String AdminSpecifiField{get; set;}
                                                  }

                                                  За регистрацию пользователя и администратора отвечают различные страницы с различными моделями. Но общие свойства этих моделей описаны интерфейсом, и для отображения этих общих свойств используется общая вьюшка.
                                                    0
                                                    Да, я вначале не понял…
                                                    Тогда это действительно неплохое решение, если подсовывать этот интерфейс в PartialView.
                                                  0
                                                  В связи со всем прочитаным у меня возникло 2 вопроса:

                                                  1. А как же локализация и ресурсы?

                                                  Предположим локализация берется через интерфейс IResource с функцией string GetString(string)
                                                  Во вьюшке есть несколько локализованых строк.
                                                  — Вроде как в модели локализации делать нечего (или я не прав)?
                                                  — Или как вариант использовать расширение к Html. Не посчитается ли тогда, что вьюха шибко поумнела?

                                                  2. К вопросу о LayoutModel. А что делать, если в контроллере написано как-то так (стырено из генеренного AccountController):
                                                          // **************************************
                                                          // URL: /Account/LogOn
                                                          // **************************************
                                                          public ActionResult LogOn()
                                                          {
                                                              return View();
                                                          }
                                                  

                                                  Тоесть другими словами — модель = null, но при этом вьюха принимает вполне строгий тип? Хотя подозреваю, что рефлекшеном можно вытащить что угодно, но имхо это не самый лучший вариант.
                                                    +1
                                                    Моё мнение по первому вопросу — это тот случай, когда кодогенерация удобна и уместна. Впрочем, это вопрос не столько программирования как такового, а, скорее, предпочтения определённого набора инструментов. Я стараюсь избегать привязки к каким-либо ключам, а всё строго типизировать, в таком случае решарпер и студия значительно помогают.
                                                    Потом не стесняюсь писать прямо во вьюшке что-то вроде
                                                    @MyApplication.Resources.Localization.Common.LogIn

                                                    Что касается второго вопроса — то я свято верю, что в данном случае именно вьюшка задаёт условия и соглашения, при которых её можно использовать. И если контроллер не в эти соглашения выполнить — нужно изменять контроллер, а не вьюшку.
                                                    0
                                                    А вот как уважаемая компания относится к тому, чтобы папки организовывать не по принципу разделения классов на роли (контроллеры сюда, модели туда), а по фичам? Я тут попробовал, и все гораздо удобнее стало. Работаешь над какой-нибудь фичей, все файлы в одном месте, под рукой. Кроме вью, за ним все время лазаешь в другую папку, и это раздражает. Но это можно вылечить созданием своего ViewEngine.
                                                      0
                                                      Примерчик по фичам можно?
                                                        0
                                                        Папка Profile, там лежит ProfileController, ProfileQuery, ProfileEditModel, ProfileViewModel
                                                        0
                                                        Общественность в моем лице относится к этому крайне положительно. Удобство работы возрастает в разы, структура улучшается и т.д.
                                                          0
                                                          Мда, вот я тормоз… :)

                                                        Only users with full accounts can post comments. Log in, please.