Комментарии 71
Предлагаемая архитектура позволяет масштабироваться проекту (и, при необходимости, сужаться) при малых затратах благодаря малой связности компонентов системы.
Действительно, это выглядит как оверхед на примере в статье, но ведь контент рассчитан на читателей, которые уже столкнулись с данными проблемами, и была надежда, что сложные примерны ни к чему. Удачного вам остатка дня!
Подскажите, зачем сейчас использовать Razor?
Вы не знали, что Javascript победил?
Да, все становится чуть сложнее, но такие задачи решаются куда меньшей кровью.
А изоморфный рендеринг?
Только в воображении сектантов. Клиентского рендеринга должен быть абсолютный минимум.
Сейчас сектант — это вы
Ну и для «особо буйных» ничто не мешает реализовать IViewEngine
На вопрос «зачем его генерировать run time » — ответ такой же — затем же зачем в dev time… Если у вас нет потребности генерировать код в дев-тайм, у вас не будет потребности его генерировать в ран-тайм. Ран-тайм это такой дев-тайм только вам не надо заниматься контролем изменений сгенерированных файлов (но надо заниматься их кэшированием).
Вот только в run time существует альтернатива генерации шаблонов.
Особо странно что Razor то как раз имеет компиляцию и прикомпеляцию и по сути Razor мог быть легко промежуточным кодом.
более строгой систематизации кода, позволяющему метапрограммированию решать все более сложные/общие задачи
За счет чего?
П.С. Мы говорил о IViewEngine я помню. Все же это решение без Razorа
Зачем ей возвращать целый шаблон если можно вернуть Func<T, HelperResult>
?
И почему вдруг IViewEngine — это без Razor? Кто-то запрещает его использовать или как?
Я могу доверять в этом ServiceStack которые утверждают что это (Razor в runtime) это не возможно?
Вы используете в своей практике T4, Expression Trees или roslyn? Я не о том что мол «если не используете не о чем с вами говорить». Просто я до сих пор не понял вашу позицию. «вернуть Func<T, HelperResult>» это аргумент против кодогенерации вообще а не кодогенерации ран тайм.
Я могу доверять в этом ServiceStack которые утверждают что это (Razor в runtime) это не возможно?
Либо вы не так поняли, либо лучше им не доверять...
Просто я до сих пор не понял вашу позицию. «вернуть Func<T, HelperResult>» это аргумент против кодогенерации вообще а не кодогенерации ран тайм.
Мне не нравится кодогенерация там где от нее нет никакой выгоды.
А когда вы давеча говорили что «в Razor нет eval из коробки» что вы имели ввиду?
Вы используете (вам нравится) возможность генерировать шаблоны Razor девтайм используя T4?
а с мнением что генерировать разор — даже дев тайм — это извращение я не спорю, хватает показать, что оно очевидно спорно — Microsoft сам использует T4 для генерации razorа. Такие T4 входят в каждый дистрибутив Visual Studio.
// upd: превентивно изменил название, надеюсь, так будет более уместно.
Если данный генератор кода будет универсальным в предельно общем смысле, то его настройка будет более сложна, чем механическое написание данного кода.
Это из той же серии (утрированно), что и написание некоторой универсальной системы в принципе.
// Когда-то я с товарищами задумал написать программный комплекс, эмпирически решающий подавляющее большинство типичных задач по ТВиМСу с помощью моделирования на основе входных данных, да так, чтобы любой неподготовленный человек сумел воспользоваться. Однако, на стадии прототипирования стало понятно, что настройка и формирование входных данных на комплекс — задача более трудоемкая, чем «по-быстрому» закодить это на том же Python'e и посмотреть результаты. Это впоследствии послужило большим уроком в аспекте универсальных подходов.
А вот шаблоны, гайдлайны — приветствую, ибо там действительно одно и тоже (и это хорошо!). Соответственно, написать генератор кода по заданным шаблонам — пожалуйста.
Возможно ведь и такое рассуждение — решение ценно — если вы решили достаточно общую задачу?
Чтобы искомый генератор «знал» о данных правилах, их нужно описать. Описать в формализованном и общем виде. А данная операция, на мой взгляд, едва ли менее трудоемкая, чем сразу написать соответствий DTO вручную (это и будет, в частности, манифест который требуется «на вход» генератору).
Однако, в действительности можно представить, что вы в используемой IDE графически выделяете нужные свойства в модели, нажимаете кнопку генерации и получается соответствующий DTO-класс, тогда неплохо.
С контроллером вообще все сложно (впрочем, CRUD функции можно сгенерировать в некотором общем виде для сущности, и сопутствующие сервисы тоже, тут вы правы).
Уверен, что это в любом случае задача IDE.
Также речь не только об TextBoxFor, речь о любом контроле, который поддерживается стандартной реализацией HtmlHelper.
Однако, после ваших слов, ухожу на очередную итерацию, направленную на более подходящее название. Спасибо!
Используйте в полной мере доступный функционал — есть ведь прекрасные PartialView, ChildAction(в ASP.NET) и ViewComponent(в ASP.NET Core).
Появится у вас задача на той же страничке, отобразить еще какой-то блок информации, не будете же вы и её добавлять во ViewModel?
Помимо того, что вы разделяете логику и отображение, вы еще и избавитесь от обеих ваших проблем
У них имена свойств должны синхронизироваться. Разве нет?
Есть например у viewmodel свойство 'телефон', хелпер создаст input и сгенерирует имя и ид на основе модели, потом пользователь отправляет post на сервер, мы же должны передаем body запроса с уже сформированными полями, соответственно нам нужно знать что сгенерировано В браузере, чтобы понять как нам маппить входные данные на commandDto
Почему пустое, мы там например текущее показываем
Эх, было время когда мы так же писали. Но прошло время, поменялся подход и из проекта выкинули примерно 60% кода с учетом того, что полезные для пользователя функции добавлялись.
И у нас отлично уживается Razor с Vuejs в SPA.
Ужас какой, нет конечно. На Vuejs реализованы сложные части UI, которые через обычный DOM+jQuery сделать конечно можно, но получается на порядок сложнее Vuejs компонент.
Vuejs компоненты являются частью других компонент, которые могут иметь свой Razor шаблон, так и рендерится обычними helperами. SPA работает с использованием HistoryJS и получает от сервера HTML, в котором может использоваться VueJS.
class TransportAddViewModel
{
// public TransportAddDTO AddDTO { get; set; }
[Required]
public int TransportTypeId { get; set; }
[Required]
[MaxLength(10)]
public string Number { get; set; }
public IEnumerable<TransportTypeDTO> TransportTypes { get; set; }
}
Т.е. сам ViewModel является моделью для отображения данных + нужные словари. Контроллер ожидает ваш TransportAddDTO (с такими же полями). Явный минус такого подхода — дублирование атрибутов. Но их приходится дублировать и в Entity.
Можно с вашей моделью использовать html tags и явно указывать Name:
<input name="Number" asp-for="@Model.AddDTO.Number">
Но как вы там устраиваете биндинги — имеет столь малое практическое значение, что огорчаться увидев другое устройство биндингов — не имеет никакого смысла.
Все свободны даже (о боже!) передавать словари через ViewBag кто бы что не говорил. И парсить HttpRequest в свое удовольствие без всяких биндингов на реквест. Во многих случаях будет просто тупо меньше кода, что уже хорошо.
На данном этапе встает резонный вопрос: в Razor можно будет передать только одну модель (и слава богу), как же тогда использовать TransportAddDTO для генерации HTML-кода внутри данной страницы?
Очень просто! Достаточно в View Model добавить, в частности, данный DTO
Простой путь не самый верный.
Почитайте docs.microsoft.com/ru-ru/aspnet/core/mvc/views/dependency-injection?view=aspnetcore-2.0
Я бы делал как-то:
@model Transport
@inject DictionaryService dictionary
@{
var AvailableTransportTypes = dictionary.GetAvailableTransportTypes();
}
@Html.TextBoxFor(m => m.Number)
Впрочем ещё есть вариант с ViewBag, но менее удобен.
Второй момент — часто DTO это никому не нужный boilerplate. На некоторые атрибуты достаточно повесить атрибут [Ignore], чтобы их сериализатор пропускал. В крайнем случае DTO отнаследовать от сущности. Атрибуты валидации, я бы тоже повесил на доменную сущность.
Разве присваивание словаря ViewBag'у — не происходит в контроллере и является кодом контроллера? Почему вдруг `@inject` в Razore вдруг привел к «разделению логики на контроллера и отображения»? Или Вы как и я за реабилитацию ViewBag (реабилитацию в смысле «как вам удобней»)?
this.ViewBag.MyDictionary = dictionaryService.GetDictionary();
return service.GetEntity();
Разве тут не «разделина логика»?
поправка:
this.ViewBag.MyDictionary = dictionaryService.GetDictionary();
return View("Details", service.GetEntity());
Вызов dictionaryService.GetDictionary не относится к обработке ввода.
Вы как программист, конечно же, вольны делать что угодно. Но это будет уже не MVC.
MS в своем коде передает словари через ViewBag — разве это не MVC?
Ну вот я написал так контроллер с разделенной логикой — все на два сервиса. Почему она не разделена? «Контроллер — обрабатывает ввод» — это никак не противоречит. Реквест (не знаю что такое «ввод») пользователя вынуждает поднять словарь для select'а, контроллер и поднял, обработал.
ASP.NET Razor: решение некоторых проблем с архитектурой для модели представления