Иными словами, Razor Pages — более вменяемое решение для веба чем MVC, теперь мы имеем дело с традиционным и логичным понятием «страница» а не с контролерами и моделями разбросанными по всему проекту. Но поскольку .NET будет развиваться по направлению Core.Net то вряд ли Razor Page появятся в стандартном фреймворке, несмотря на то что ближайшие годы большинство проектов будет оставаться на стандартном .NET. Тем не менее можно изобразить функциональность Razor Pages и на стандартном фреймворке.
Решение на самом деле выглядит достаточно тривиально — нужно добавить в контролер следующую конструкцию:
protected override void OnActionExecuting(ActionExecutingContext Context)
{
UpdateModel(this);
}
Метод OnActionExecuting -событие жизненного цикла который вызывается перед выполнением метода контролера (имеется ввиду обработчик запроса — Action).
UpdateModel выполняет непосредственно привязку параметров запроса к свойствам (properties) модели — в данном случае свойствам класса контроллера.
Дополнительное удобство — теперь нет необходимости вообще принимать явно параметры ни типа Model ни какие другие. Хотя ничего не мешает это делать, если параметр — простой id, который будет использовать чисто как локальная переменная, но привязка параметров как свойств контролера необходима например, если нужно обеспечить персистентность страницы, о чем пойдет речь дальше.
Простой пример:
У нас есть обычная форма логина с двумя полями.
Приводить разметку нет смысла приведу только код контроллера
public class AccountController : Controller
{
public string username{ get; set; }
public string userpass{ get; set; }
[HttpPost]
public ActionResult OnLogin( )
{
//некая функция проверки в БД
checklogin(username,userpass);
return View("Index",this);
}
protected override void OnActionExecuting(ActionExecutingContext Context)
{
UpdateModel(this);
}
}
Как видим, в момент срабатывания события входные данные уже привязаны и готовы к использованию.
Разумеется, надо иметь ввиду что теперь возвращать как ActionResult тоже нужно контроллер ну и в шаблоне прописать имя класса контролера — типа @Model AccountController.
Как следствие такого решения упрощается и задача сохранения состояния страницы между запросами. Допустим у вас есть страница с некоей таблицей, фильтром сортировкой по столбцам и пагинацией.
Вы кликаете по фильтру модель возвращается в представление и все хорошо, но когда кликните сортировку фильтр естественно сбросится. Пагинатор сбросит и сортировку и фильтр. В WebForms состояние страницы сохранялось автоматически, в MVC приходится применять разные громоздкие решения, например склеивать все параметры и гонять их по каждому запросу, то есть на ссылку сортировки нужно повесить все параметры которые пришли перед этим с фильтра.
Подобные затруднения являются одной из причин SPA и прочего яваскриптового безумия с перетаскиванием логики и данных в браузер с вытекающими из этого тормозами браузера (особенно мобильных девайсах), прыгающими и дергающимися за каждым движением мышки страницами и увеличением трудоемкости и стоимости работ — ведь бэкенд то все равно писать в том или ином виде, упаковывать распаковывать данные пересылаемые через ajax, плюс callbac hell, усложнения отладки и так далее, и все это без практической пользы для посетителей сайта которым нет дела как написана страница.
Наиболее логичным решением является сохранение состояния страницы в сессии. Поскольку у нас все необходимые параметры уже под руками в виде свойств контроллера, все что нужно — это переопределить метод OnActionExecuted, который вызывается после обработки запроса, и упаковать нужные свойства в сессию (ключ сессии очевидно должен указывать на имя контролера).
protected override void OnActionExecuted (ActionExecutedContext Context)
{
//сохраняем данные
}
Восстанавливаются параметры из сессии в конструкторе контролера или перед вызовом UpdateModel(this). Когда придет запрос, например сортировки то новые параметры изменятся а остальное останется нетронутым и представление будет отображено в том же виде как было отправлено.
Такое решение имеет еще одно удобство — к примеру, пользователь отсортировал таблицу, и решил какой то элемент отредактировать, открывши для этого другую страницу. Естественно он хочет вернутся в то состояние списка которое оставил а так как состояние страницы у нас в сессии, то страница восстановится автоматически. Нет никакой необходимости, как часто делают, передавать весь «вареник» параметров в страницу редактирования и обратно. Если сохранять состояние между страницами нет необходимости то состояние страницы можно хранить не в сессии а в TempData.
Надеюсь данные «лайфхаки» хоть и выглядят тривиально, будут полезны новичкам пока они не подсмотрели в интернете более неудобных и громоздких решений и решили что других не бывает.
Комментарии (31)
mayorovp
27.08.2019 14:14Теперь по вашей реализации.
Решение на самом деле выглядит достаточно тривиально — нужно добавить в контролер следующую конструкцию
Откуда у нас вообще взялся контроллер, если у нас, вообще-то, Razor Pages?
Наиболее логичным решением является сохранение состояния страницы в сессии.
Но это сразу же делает невозможным работу в двух окнах браузера!
Почему бы просто не добавить на страницу нужные скрытые поля?
В WebForms состояние страницы сохранялось автоматически, в MVC приходится применять разные громоздкие решения, например склеивать все параметры и гонять их по каждому запросу, то есть на ссылку сортировки нужно повесить все параметры которые пришли перед этим с фильтра.
Это вообще не проблема. Это решение как раз и является "родным" для веба.
caballero Автор
27.08.2019 14:35Откуда у нас вообще взялся контроллер, если у нас, вообще-то, Razor Pages?
Razor Pages это в Core. Суть в том как это сделать в стандартном ASP.NET MVC фреймворке. При желании конечно можно убрать контроллер — то есть переименовать его и может даже переместить, но смысла особого в этом нет.
Но это сразу же делает невозможным работу в двух окнах браузера!
не думаю что люди часто работают с одной и той же страницей в двух окнах (тем более это чревато работой с неконсистентными данными). Обычно работают с РАЗНЫМИ страницами одного и того же сайта.
Почему бы просто не добавить на страницу нужные скрытые поля?
потому что не все запросы отправляют форму. Это в WebForms вся страница бла формой и все запросы были POST
Это решение как раз и является «родным» для веба.
Нет — родным для веба является как раз stateless то есть проблема, а решения бывают разные. Согласен, в большинстве случаев тупо гоняют вереницу параметров.
Об это и речь, что можно делать и по другому, резко упростивши количество кода.
mayorovp
27.08.2019 14:48+1Razor Pages это в Core. Суть в том как это сделать в стандартном ASP.NET MVC фреймворке.
А зачем вы ограничиваете себя MVC фреймворком? Напомню, что Razor Pages, которые в Core, с MVC никак не связаны.
caballero Автор
27.08.2019 15:04Ну технически Razor Pageв Core.NET строится поверх MVC, поэтому обязательно требуется использовать useMvc в Startup.cs
Но суть как раз в том чтобы упростить стандартный (не Core.NET а обычный .NET) MVC фреймворк где RAzor Page нет и вряд ли будет.. Потому что на нем пишут 99 из ста приложений. Не MVC решения очевидно не обладают недостатками которые исправляет решение аналогичное Razor Pages
Kanut
27.08.2019 22:05Суть в том как это сделать в стандартном ASP.NET MVC фреймворке. При желании конечно можно убрать контроллер — то есть переименовать его и может даже переместить, но смысла особого в этом нет.
Суть в том что по большому счёту смысла нет во всей вашей идее. То есть прикольно поиграться и выяснить что да, оно действительно так можно сделать. Но вещи в продакшен я бы на таком никогда писать не стал.
И если вас не устраивает MVC, что я очень даже понимаю, то наверное стоит посмотреть на имеющиеся и/или запланированные на будущее альтернативы, а не пытаться изобретать непонятно что…caballero Автор
28.08.2019 05:57а какие проблемы с этим в продакшене? И это не моя идея — Razor Pages в Core не я добавил. Я просто показал как можно это повторить в стандартном фреймворке.
В этом и преимущество разработки сообществом в опенсорсе коим ща является Core.Net — там не только те кому на курсах программирование рассказали что MVC это священная корова и по другому програмировать не моги или разрабы мелкомягких которым наплевать как на юзеров так и на других разрабов которые этим будут пользоватся.
на имеющиеся и/или запланированные на будущее альтернативы
Razor Pages и есть альтернатива на данный момент и пока я не услышал ТЕХНИЧЕСКИ обоснованных аргументов почему это будет работать хуже MVC. Компилятор сгенерит почти тот же код — удобства тут именно с точки зрения разработки — меньше индусского кода.
.
Kanut
28.08.2019 08:52+1Во первых не надо всех налево и направо обвинять в том что они считают MVC «священной коровой» и не могут себе представить веб без MVC.
Я MVC сам не особо люблю и потратил много усилий чтобы убедить начальство его больше не использовать в наших проектах.
Но всё равно надо признать что MVC это известный паттерн, а .Net MVC известная технология, которая легко понятна любому джуну и вполне себе имеет свои области применения.
Во вторых если вам нравятся Razor Pages и вы считаете что именно за ними будущее, то переходите на эти самые Razor Pages. Но вы вместо этого пытаетесь «перекрутить» MVC так, чтобы он стал похож на Razor Pages. И именно это на мой взгляд является очень плохой идеей. И на мой взгляд это из области вот таких вот «кастомных решений»:
+caballero Автор
28.08.2019 11:30Но всё равно надо признать что MVC это известный паттерн, а .Net MVC известная технология, которая легко понятна любому джуну и вполне себе имеет свои области применения.
Ну страница с бекендом а-ля Razor Pages джунам будет как раз понятнее.
Но вы вместо этого пытаетесь «перекрутить» MVC так, чтобы он стал похож на Razor Pages.
Ну так в стандартном фрейморке Razor Pages нет. А для приведенного решения ничего переделывать не надо кроме конкретного контролера то есть можно так доделавыть или переделывать существующий проект на стандартном нет.
Потому что вы протестировали вашу идею на плоской форме с двумя полями, но с такой формой и MVC справляется без проблем.
конечно справляется — проблема не в этом а в том что приходится использовать лишнюю прослойку — модель страницы смысла в которой нет. А на сложные страцы бывает и по несколько моделей городят.
Потому что новичкам нельзя будет дать мануал по MVC или Razor Pages, а надо будет объяснять как
и не нужно. Это решение для тех кто уже в теме но хочет оптимизировать свой проект.
Ну или представьте себе что вы по какой-то причине уйдёте из фирмы, а вашему преемнику придётся во всем этом разбираться без посторонней помощи…
когда кто то уйдет и придется разбиратся — это будет не самая большая трудность. Обычно в серьезных проектах основная трудность — это бизгнес-логика, ообенно когда проекту не первый год, поменялось три команды а заказчик и сам уже не помнить зачем применялся тот или иной алгоритсмм.
И вообще новые решения и технологии ща появляются каждый день — кто не умеет разбираться самостоятельно и осваивать новое тому в IT делать нечего.
.Kanut
28.08.2019 11:49Ну страница с бекендом а-ля Razor Pages джунам будет как раз понятнее.
Если она местами будет выглядеть и вести себя как MVC, местами как Razor Pages, а местами как непонятно что? При отсутствии нормальных мануалов и помощи на каком-нибудь stackoverflow? Что-то я в этом сомневаюсь.
Ну так в стандартном фрейморке Razor Pages нет.
И вас этот факт вообще не смущает? Ведь если оно так просто, то что например мешало самому Microsoft добавить такую функциональность? :)
И почему вообще сразу на Core не перейти?
А на сложные страцы бывает и по несколько моделей городят.
И как что-то подобное будет реализовано в вашем конструкте?
Это решение для тех кто уже в теме но хочет оптимизировать свой проект.
Это вообще пять баллов. То есть вы предлагаете уже имеющийся проект зачем-то «оптимизировать» и всё?
А дальше его развивать уже не надо будет? Если нет, то зачем его тогда оптимизировать? А если надо, то кто это будет делать? Исключительно избранные сениоры, которые «в теме»?
когда кто то уйдет и придется разбиратся — это будет не самая большая трудность.
Сколько в вашем портфолио перенятых без посторонней поддержки легаси проектов? Сколько из них имели подобные «кастомизации»?
И вообще новые решения и технологии ща появляются каждый день — кто не умеет разбираться самостоятельно и осваивать новое тому в IT делать нечего.
Новые технологии обычно имеют хорошую документацию и комьюнити, с которой можно обсуждать баги/проблемы и их решения. Есть это у ваших «MVCRazorPages»? Или есть хотя бы шансы что это появится в обозримом будущем?caballero Автор
28.08.2019 16:33-2Если она местами будет выглядеть и вести себя как MVC, местами как Razor Pages, а местами как непонятно что?
чего там непонятного? как запрограмируют так и будет себя вести. Нет никаких технических проблем сочетать в одном проекте MVC, Razor Pages и например WEBAPI.
И как что-то подобное будет реализовано в вашем конструкте?
там нет никакого конструктора. Вы вообще читали статью?
Все что было моделями теперь будет в одном классе вместе с обработчиками как принято в парадигме ООП.
При отсутствии нормальных мануалов
там несколько строк как. Какие нужны мануалы? Этой статьи более чем достаточно. Все остальное в официальных доках.
Ведь если оно так просто, то что например мешало самому Microsoft добавить такую функциональность?
Потому что стандартный фреймворк развиватся уже не будет. И майкрософту наплевать будет ли оно просто. А вот сообществу работающему над Core.Net не наплевать. Потому и появились Razor PAges/
То есть вы предлагаете уже имеющийся проект зачем-то «оптимизировать» и всё?
Проекты которые на саппорте все равно доделываются и переделываются. Почему бы по дороге их не упростить.
Сколько в вашем портфолио перенятых без посторонней поддержки легаси проектов?
Хватает. Но оно не в моем портфолиро в компании где я работаю.
Новые технологии обычно имеют хорошую документацию и комьюнити
Это наверно в вашей параллельной вселенной так.
Есть это у ваших «MVCRazorPages»? Или есть хотя бы шансы что это появится в обозримом будущем?
Это простое решение в несколько срок на основе уже имеющейся платформы. Там неоткуда взяться багам и там нечего поддерживать Там даже на гитхаб нечего выкладывать отдельным проектом.
Вы или не поняли написанного или не читали вообще а просто возражаете на все коменты подряд.
Чтобы прекратить флуд — покажите хотя бы одну ТЕХНИЧЕСКИ обоснованую проблему которая возникнет изза предлагаемого решения. Аргумент типа — у кого то не хватит извилин разобраться в несколькизхс строках состоящих из стандартных функций описанных в документации майкрософта — это не аргумент. Большинство статей на хабре гораздо сложнее.
lair
28.08.2019 16:37+1покажите хотя бы одну ТЕХНИЧЕСКИ обоснованую проблему которая возникнет изза предлагаемого решения
Сначла покажите хотя бы одно технически обоснованное достоинство предлагаемого решения.
Kanut
28.08.2019 17:10Как уже написали выше, мы в вашем решение пока не имеем ни одного технически обоснованного преимущества.
Но при этом покупаем кота в мешке что касается нормального функционирования кастомизации и её совместимости с различными сторонними решениями.
И как минимум получаем завышенный порог вхождения в проект для посторонних.
Лично для меня это уже причина оставить такую кастомизацию в стороне и либо взять .Net Core Razor Pages, либо посмотреть на другие имеющиеся альтернативы. И похоже я здесь не одинок в таком мнении. Но в конце-концов решение принимать вам.
SemenPV
27.08.2019 17:04Но это сразу же делает невозможным работу в двух окнах браузера!
Чудес не бывает, можно конечно тянуть на клиет viewstate как в webforms, но если есть хороший session manager, то можно на клиенте для каждого открытого окна иметь только guid, а данные с сервера забирать.
Проблема только с тем как правильно чистить старые данные.mayorovp
27.08.2019 17:24Проще тогда уж использовать SPA фреймворки. Или серверный Blazor.
SemenPV
27.08.2019 17:42Если вы новый проект создаёте, то конечно всё что угодно можно взять. Но если у вас уже есть проект и требуется поддержка подобной функции, то уже ничего не поделаешь, придётся прикручивать.
mayorovp
27.08.2019 19:03+1Это вы о какой ситуации говорите? О ситуации вида "раньше сайт работал только строго в одной вкладке, а теперь надо разрешить пользователю открывать вторую"?
В таком случае — да, без хитрого session manager не обойтись. Но я предлагаю в такую ситуацию просто не попадать.
mayorovp
Нет, это довольно старая фича (известная как ASP.NET WebPages), просто её наконец-то вытащили на свет.
Больше похоже на устаревший подход а-ля PHP, который уже даже в самом PHP не рекомендуется к использованию.
Впрочем, достоинства у него тоже есть, в узком случае страниц только для чтения.
caballero Автор
а в чем недостатки если страница для записи?
mayorovp
В том, что либо получается куча логики среди разметки, либо же от контроллера не удаётся избавиться.
caballero Автор
откуда куча логики в разметке? Разметка тут вообще не меняется.
И зачем избавляться от контроллера? Мы избавляемся не от контроллера а от ненужной прослойки под названием модель страницы. По сути это то что иногда называют DTO (Data Transfer Object) — обычно это костыль подпирающий неудачную архитектуру.
А контроллер превращается в бекенд страницы соответствующий парадигме ООП и просто здравому смыслу.
Я так понимаю вы из поколения программистов считающих что MVC и веб это одно и тоже. Но лично мой мой многолетний опыт програмирования как раз убедил меня в обратном — MVC (в той релизации в которой он применяется в большинстве фреймворков) — самый неудачный паттерн для веба.
mayorovp
Потому что Razor Pages и Razor Views, в том числе, отличаются наличием контроллера, и именно это отличие заметнее всего.
Если вы пытаетесь избавиться от "модели страницы" — вы решаете какую-то другую задачу, отличную от декларированной.
И да, объединить контроллер с "моделью страницы" (на самом деле моделью запроса) — совершенно не то же самое что от модели избавиться.
Судя по тому что я вижу — это как раз вы так считаете...
caballero Автор
смысл в данном случае — реализовать аналогичный функционал. как будет называтся бекенд и в какой папке он будет не суть важно.
мы избавляемся от лишнего класса — посредника, чем упрощаем код и делаем его более прозрачным. Нет ни одного разумного аргумента (кроме тупого минусования) зачем нужен класс смысл которого переложить данные с одного места в другое в пределах одного приложения.
codecity
Совсем не то. В Razor Pages во View работа с моделью, основные методы (код) в отдельном C#-классе. Т.е. присутствует разделение представления и логики.
Основная претензия к тем же JSP, старому ASP, PHP (хотя PHP и другие проблемы, более серьзеные) — это мешанина из кода и разметки. В Razor Pages эта проблема решена.
mayorovp
Там "модель" такая, что больше на CodeBehind страницы похожа...
codecity
В чем практический минус?
mayorovp
Тут не то чтобы минус, просто я как-то не вижу особых преимуществ.
codecity
Как по мне — удобно что контроллер рядышком с View. Не нужно его искать в другой папке.
Модель-контроллер в Razor Pages объединяет как данные, так и методы работы с ними. Ближе к классике ООП, но есть и свои минусы.
mayorovp
Так-то и в MVC вовсе не обязательно было придерживаться структуры папок по умолчанию.
caballero Автор
в MVC оно тоже присутствует только логика страницы размазывается по разным классам.
А вот в самом razor движке как раз с таким разделением не очень. начиная от запутаного синтаксиса (не сразу сообразишь где надо ставить @ а где нет) до разного рода Html хелперов которые затрудняют работу верстальщикам. Но это отдельная проблема самого движка как активном шаблонизатора а значит неизбежно содержащего код вперемешку с версткой. В этом плане Razor ничем не лучше PHP и JSP
codecity
В MVC тратится время, чтобы переходить от View к нужному контроллеру (т.е. навигация в процессе разработки). А в Razor Pages все рядышком…
Совмещение данных и методов, которые с ними работают (т.е. объединение модели и контроллера в один класс) — имеет как плюсы так и минусы. В любом случае подход имеет право на жизнь. Главное что View — отдельно.