Мой первый сайт на MVC 3 Razor
Возможно для меня лично сменить основную рабочую платформу хостинга с Windows на Linux и заодно и сменить SQL-сервер (чтобы начать делать сайты в MONO) оказалось проще, чем перейти от ASP.NET к ASP.NET MVC. В MVC утерялась простота и прозрачность Web-программирования - основные обработки QueryString (которые я делал своими собственными, упакованными в библиотеки функциями) стали теперь делаться в мутном коде обработки маршрутов, утопленнх в классе Route. Кроме того, в IIS 7 появился новый режим работы, в который тоже зашит код маршрутизации URL. В сущности мой собственный простой и понятный код в сто строчек оказался заменен сложнопонимаемым монстром, которого теперь нам навязал MS - и под который теперь всем придется подстраиваться.
Пропала также красота и лаконичность бейсика 2005, в нем появились странные синтаксические конструкции - типа анонимных функций, лямбда-выражений и языковых конструкций Linq. Появились новые чемоданы знаний, которые надо таскать с собой чтобы программировать на бейсике, например концепция ковариация/контрвариация. А этих чемоданов в языке и так прибавлялось немеряно каждый год (считая от Visual Basic 6). Хотя, сознаюсь, кое что меня в новой версии бейсика откровенно порадовало - прямой замес XML в код бейсика и автопродолжение команды на следующую строчку.
Более ограниченным и убогим стал функционал Visual Studio 2010 по сравнению с предыдущей Visual Studio 2008. Начиная от того, что нас кинули с обещанной API поддержки в NET формата MS HELP. И вот нате-здрасте - в VS2010 help в привычном виде с перечнем топиков изчез вообще. Правда это возможно вылечить сторонней прогой H3Viewer. Более органиченной стала и бесплатная редакция Visual Studio. Теперь в бесплатной версии Visual Stusio 2010 сайты делать уже нельзя - скомпилировать сайт в указанную директорию и выложить ее на хостинг не разрешается. В бесплатной Visual Studio 2010 Express Edition разрешается только немного поиграться с собственным кодом на локальной машине. Зато потребление ресурсов в VS2010 вдвое увеличилось по сравнению с VS2008 и на старых кампутерах все работает еще медленнее.
У новой микрософтовской студии есть и некоторые положительные моменты, сокращающие отставание микрософтовской девелоперской среды от бесплатных OpenSource сред разработки, таких как NetBeans и Eclipse. Менеджер расширений в новой Visual Studio 2010 уже почти такой же как в NetBeans. Правда не настолько продвинутый функционально. В NetBeans можно каждое отдельное расширение догружать из самой среды (с предварительным просмотром классов), отдельно сбрасывать на диск и подключать из IDE позже или вообще вручную в XML-файл дополнений. Можно четко видеть версии всех библиотек для каждого функционального дополнения, видеть зависимости функциональных расширений друг от друга, менять любые версии дополнений. Пока такого делать в микрософтовкой студии нельзя - но радует что менеджер дополнений наконец-то хотя бы появился.
В Visual Studio 2010 (для движка RAZOR) вообще исчез визуальный дизайнер ASP.NET, а для движка ASPX визуальный дизайнер превратился в некий бессмысленный предпросмотр (аналогичный браузерному) - в сущности мы опять вернулись к программированию в Нотепаде. Если бы не подсветка и подсказка - программирования в MVC в точности совпадало бы с программированием в Нотепаде. А ведь вся идея ASP.NET была в том, чтобы навсегда уйти от программирования в Нотепаде, уйти от огромных и непрогнозируемо работающих за кулисами библиотек (типа библиотеки маршрутизации в MVC), создать технологию быстрой визуальной разработки. В сущности для этого в ASP.NET работает лишь один JAVASCRIPT-код и ASP.NET-конвеер, который вполне прогнозируемо увязывает клиенские события с серверным кодом. Теперь старого ASP.NET конвеера Page_Load->Comtrol_Load->MasterPage_Load уже нет. Хотя конечно этого конвеера не жалко - уж больно гнилой он был, когда надо было увязать множество вложенных контролов, саму страничку и masterpage. Но я (и все остальные) довольно таки неплохо научились с ним работать за последние восемь лет. Но теперь этого конвеера из событий странички уже нет - и все на чем набивали руку последние восемь лет - навсегда исчезло.
В MVC код разметки страницы опять превратился в адскую лапшу из тегов разных языков - html-теги прихотливо перемешиваются со странными директивами бейсика (новой версии). Все что мы уяснили для себя при переходе от простого ASP к ASP.NET в 2002-м году - нет ничего хуже лапшеобразного кода (когда впеременку идет html-разметка и поверх нее код бейсика For Each). Теперь опять - да здравствует лапша из кода разных языков! Если уж так, то лучше бы напрямую (без всякого Linq) черезстрочно мешали бейсик и SQL, чем додумались замешивать венегред из бейсика и html.
Теперь в MVC предполагается что для отображения страничке подается обьект, который надо предварительно обязательно приготовить - а потом странным мастером создать View (шаблон странички), который предназначен для отображения именно этого обьекта. В начале страничек теперь появились странные директивы, описывающие какой именно обьект способна отображать эта страничка. При чем в web-программировании вообще обьекты? Ведь браузер передает на сервер в виде постбека просто строку текста, Web-сервер должен принять эту строку текста и сформировать новую html-строку. В ASP.NET обьекты обрабатываются в коде и отдаются либо контролам в виде коллекций, либо ASP.NET страничка может вообще состоять из одного тега literal, в котором собтсвенным ASP.NET-кодом можно сформировать любой нужный html. Нелишне напомнить, что наиболее распространенные и быстрые языки вообще не поддерживают обьекты - например Perl или SQL. С этой точки зрения - подход MVC это какой-то обьектный экстремизм за гранью разумного.
Правда, кое что в MVC меня порадовало. Опять (как в простом ASP) - можно создавать странички из нескольких тегов forms. При переходе в 2002-м году с ASP на ASP.NET у меня именно наличие единственного тега forms в ASP.NET вызвало наибольший внутренний протест. Но, увы, вместо доработки ASP.NET (в части усовершенствования конвеера, поддержки множества тегов forms, поддержки фреймов и так далее) - микрософт в очередной раз нас всех тупо кинул - и бросил разработку ASP.NET вообще. Похоже MS наняло каких-то индусов (никогда не слышавших об ASP.NET) и, видимо, эти индусы сделали для MS совершенно иной продукт - MVC (никак не связанный с существущими технологиями MS). А отдел маркетинга MS просто прикрутил к названию нового продукта префикс ASP.NET (чтобы сохранить видимость преемственности и сгладить впечатление от кидка).
Итак, далее я покажу как я сделал свой первый сайт на MVC 3 RAZOR. Это может послужить хорошим step-by-step руководством для тех, кому тоже придется отказаться от своих наработок на ASP.NET и с нуля начать Web-программирование в парадигме десятилетней давности. При этом на тех деталях, которые в новой логике программирования совпадают с ASP.NET, я останавливаться не буду (собственно разметка страниц и код классов моего сайта), а покажу лишь ключевые моменты MVC-программирования, которых не было в ASP.NET.
Движков шаблонов страниц (View) в MVC существует два - ASPX и RAZOR. Первый хоть и носит название ASPX, но к ASPX прямого отношения не имеет - гораздо больше напоминает древний движок простого ASP из Visual IntedDev. RAZOR движок произвел на меня более приятное впечатление - поэтому этот сайт я сделал на нем.
Сайт в NET 4.0 (как и в NET 2.0) можно делать в проектной и в беспроектной архитектуре. В беспроектной это просто каталог - как в ASP.NET и у него обычный global.asax из простого ASP и ASP.NET. В проектной архитектуре есть некое подобие свойств Win-приложения, другой конвеер и другой Global.asax. Далее я буду делать все в проектной архитектуре, а ненужные файлики примера MVC-сайта удалю потом. Разверну я свой сайт в IIS6.
Далее я выполняю все предварительные косметические действия по настройке шаблонного проекта. Добавляю папочки WebClient и Images, в которой лежат нужные мне работы для сайта рисуночки, файлик со стилями проекта, убираю ненужную папку для MDF-файла, из которой умеет подхватывать базы SQLExpress, а вместо этого устанавливаю нормальную connectionstring к моей версии MS SQL.
Вставляю волшебные строчки, чтобы в этом проете могли работать ASHX-хандлеры и ASPX-странички. Я выбрал себе для работы имя контроллера Payment - поэтому его и впишу в имя дефолтного правила для обработки URL. После создания шаблона сайта его можно попробовать запустить - чтобы увидеть html-форму стандартного приложения.
Весь цикл создания странички начинается с модели - для чего для своей первой странички я создаю модель в виде простейшего класса из одного поля. Далее надо обязательно первый раз откомпилировать приложение, чтобы появились классы в подсказке.
Далее смотрю, как в движке Razor стартовая страничка приложения запускает мастер_page и вписываю туда свою начальную страничку приложения.
Создаю папку Payment для шаблонов страниц и добавляю туда шаблон страницы Index.vbhtml (после первой компиляции классы для подсказки уже есть). При создании стартовой странички указываю мастер-пейдж проекта M1.vbhtml - появляется первая шаблонная страничка, теперь надо создать шаблон Master-page, который был указан для этой странички. Это обычная html-страничка, в теле которой есть @RenderBody - в это место будут включено тело шаблона Index (и других).
Теперь создаю собственно первую простейшую страничку проекта Paymetnt/index.vbhtml. Модель, по которой работает шаблон странички указана в директиве @ModelType, поэтому все работает с подсказкой.
Теперь создаю контроллер для заполнения этого шаблона - в нем должно быть минимум Index (Page_load для начального заполнения странички) и указанный мною метод SetBarcode, который примет Postback от странички. Это будет начальное правило по умолчанию в моей таблице URL-маршритизации. Имя контроллера (в соответствии с правилом роутинга URL, прописанным в Global.asax) должно совпадать с именем папки с View - шаблонами страниц.
Создаю код метода SetBarcode, в котором принимаю переменную отправленную с html-странички.
Теперь можно попробовать запустить проект. Ура, первая страничка с заглушкой вместо модели работает - проверяю как контроллер ловит постбек.
Постбек проходит нормально (дальше видно что следующей странички еще нет) - значит начинаем делать вторую страничку с более сложной моделью.
Для начала создаю модель, с которой будет работать мой шаблон SetBarcode. У меня есть SQL-база, по которой будет работать страничка.
В ASP.NET MVC модели реляционных данных бывают двух типов - Entity framework и Linq to SQL. Entity framework считается последней, более сложной и универсальной версией. В этом проекте мне это совсем не нужно, зато нужна прямая поддержка XML. Поэтому решаю делать на более простом Linq to SQL.
У меня есть табличка MobileOperator, есть вьюшка, которая парсит XML в этой табличке и есть пейджинговая процедурка GetOperatorPage, которая выдает нужную страничку данных из этой процедурки. Создаю метод GetOperatorPage который должен выдавать обьекты типа GetOperator - на этом все, модель для изготовления шаблона (View) готова.
Теперь создаю автоматически генерируемый шаблон странички OperatorPage для отображения списка результатов, выдаваемых процедуркой GetOperatorPage.
В контроллере указываю новое View - куда будет переход после постбека и набор данных, который сгенерирован вызовом процедуры GetOperatorPage запускаю страничку - все как-то запускается и отображается.
Это простейший вариант обработчика события. В принципе обработчик можно вернуть следующие результаты:
- ActionResult - Инкапсулирует результат метода действия и используется для выполнения операции уровня инфраструктуры от имени метода действия.
- ViewResult - Представляет класс, используемый для визуализации представления с помощью экземпляра IView, который возвращается объектом IViewEngine.
- EmptyResult - Представляет пустой результат, например метод действия контроллера, который ничего не возвращает.
- PartialViewResult - Представляет базовый класс, используемый для отправки в ответ частичного представления.
- RedirectResult - Управляет обработкой действий приложения, выполняя перенаправление на указанный универсальный код ресурса (URI).
- HttpNotFoundResult - Определяет объект, используемый для указания того, что не удалось найти запрошенный ресурс.
- HttpStatusCodeResult - Предоставляет метод для возвращения результата действия с определенным кодом состояния и описанием HTTP-ответа.
- HttpUnauthorizedResult - Представляет результат несанкционированного HTTP-запроса.
- JsonResult - Представляет класс, используемый для отправки в ответ содержимого в формате JSON.
- JavaScriptResult - Отправляет в ответ содержимое JavaScript.
- ContentResult - Представляет определенный пользователем тип содержимого, являющийся результатом выполнения метода действия.
- FileContentResult - Отправляет в ответ содержимое двоичного файла.
- FilePathResult - Отправляет в ответ содержимое файла.
- FileStreamResult - Отправляет в ответ двоичное содержимое, используя экземпляр Stream.
- ValueProviderResult - Представляет результат привязки значения (например, из данных отправки формы или строки запроса) к свойству аргумента метода действия или к самому аргументу.
- ViewEngineResult - Представляет результат поиска обработчика представлений.
- RedirectToRouteResult - Представляет результат, который выполняет перенаправление, используя указанный словарь значений маршрута.
- Redirect - Создает объект RedirectResult, перенаправляющий на указанный URL-адрес.
- RedirectPermanent - Возвращает экземпляр класса RedirectResult со свойством Permanent, имеющим значение true.
- RedirectToAction - Перенаправляет заданное действие, используя имя действия.
- RedirectToActionPermanent - Возвращает экземпляр класса RedirectResult со свойством Permanent, имеющим значение true, используя заданное имя действия.
- RedirectToRoute - Перенаправляет на заданный маршрут, используя заданные значения маршрута.
- RedirectToRoutePermanent - Возвращает экземпляр класса RedirectResult со свойством Permanent, имеющим значение true, используя указанные значения маршрута.
В целом контроллер программируется просто, никаких особенностей по сравнению с ASP.NET нет - доступны обычные User, Session, Server, Request, Response , на входе в параметрах контроллеру поступает FormCollection, также что-то может поступить (и можно передать) через ViewDataDictionary. В контроллере нужно только одним из вышеперечисленных методов сформировать на выходе требуемый результат, которым нужно накормить View (или редиректнутся).
Другое дело - View. В MVC присутствует много совершенно новых объектов, которых не было в ASP.NET. Например используемый мною литерал Model совсем не является ключевым словом (как может показаться сначала), а является обычным свойством странички:
- Adapter - Возвращает конкретный адаптер браузера для элемента управления.
- Ajax - Получает или задает объект AjaxHelper, используемый для визуализации HTML в сценариях Ajax.
- Application - Возвращает объект HttpApplicationState для текущего веб-запроса.
- AppRelativeTemplateSourceDirectory - Возвращает или задает относительно приложения виртуальный каталог объекта Page или UserControl, который содержит этот элемент управления.
- AppRelativeVirtualPath - Возвращает или задает путь к приложению, каталогу файла, в котором анализируется и компилируется элемент управления.
- AspCompatMode - Инфраструктура. Устанавливает значение, указывающее, может ли страница выполняться в однопотоковом апартаменте.
- AsyncMode - Инфраструктура. Возвращает значение, определяющее, как обрабатывается страница - синхронно или асинхронно.
- AsyncTimeout - Инфраструктура. Возвращает или задает значение, указывающее лимит времени при обработке асинхронных задач.
- AutoPostBackControl - Возвращает или задает страничный элемент управления, используемый для выполнения обратной передачи.
- BindingContainer - Инфраструктура. Возвращает элемент управления, который содержит привязку данных элемента управления.
- Buffer - Инфраструктура. Возвращает или задает значение, указывающее, буферизуется ли вывод страницы.
- Cache - Возвращает объект Cache, связанный приложением, в котором находится страница.
- ChildControlsCreated - Возвращает значение, которое указывает, создан ли дочерний элемент управления серверного элемента управления.
- ClientID - Получает идентификатор элемента управления для HTML-разметки, созданной ASP.NET.
- ClientIDMode - Получает или задает алгоритм, используемый для создания значения свойства ClientID.
- ClientIDSeparator - Возвращает значение символа разделителя, используемого в свойстве ClientID.
- ClientQueryString - Возвращает часть строки с запрошенным URL-адресом.
- ClientScript - Возвращает объект ClientScriptManager, используемый для управления, регистрации и добавления скрипта к странице.
- ClientTarget - Возвращает или задает значение, позволяющее переопределить автоматическое обнаружение характеристик обозревателя и задать способ отображения страницы для конкретного обозревателя клиента.
- CodePage - Инфраструктура. Устанавливает идентификатор кодовой страницы для текущего объекта Page.
- ContentType - Инфраструктура. Устанавливает тип MIME HTTP для объекта HttpResponse, связанного со страницей.
- Context - Возвращает объект HttpContext, связанный со страницей. (Переопределяет Control.Context.)
- Controls - Возвращает объект ControlCollection, представляющий дочерние элементы управления для указанного серверного элемента управления в иерархии пользовательского интерфейса.
- Culture - Инфраструктура. Устанавливает идентификатор языка и региональных параметров объекта Thread, связанного со страницей.
- DataItemContainer - Получает ссылку на контейнер именования, если контейнер именования реализует класс IDataItemContainer.
- DataKeysContainer - Получает ссылку на контейнер именования, если контейнер именования реализует класс IDataKeysControl.
- DesignMode - Возвращает значение, определяющее, используется ли элемент управления на поверхности конструктора.
- EnableEventValidation - Возвращает или задает значение, определяющее, проверяет ли страница события обратной передачи и обратного вызова.
- EnableTheming - Возвращает или задает логическое значение, если задачи применимы к элементам управления производным от класса TemplateControl. (Переопределяет Control.EnableTheming.)
- EnableViewState - Возвращает или задает значение, определяющее, поддерживает ли страница свое состояние отображения и состояние отображения всех содержащихся в ней серверных элементов управления по окончании текущего запроса страницы. (Переопределяет Control.EnableViewState.)
- EnableViewStateMac - Получает или задает значение, указывающее, должна ли платформа ASP.NET проверять коды проверки подлинности сообщений (MAC) в состоянии просмотра страницы, когда страница передается обратно от клиента.
- ErrorPage - Возвращает или задает страницу ошибок, на которую переадресуется запрашивающий обозреватель в случае возникновения необработанного исключения страницы.
- Events - Возвращает список делегатов обработчиков событий элемента управления. Это свойство доступно только для чтения.
- Form - Возвращает форму HTML для страницы.
- HasChildViewState - Возвращает значение, которое указывает на наличие сохраненных параметров состояния представления у дочернего элемента серверного элемента управления.
- Header - Возвращает заголовок документа страницы, если элемент head определен с runat=server в объявлении страницы.
- Html - Получает или задает объект HtmlHelper, используемый для визуализации элементов HTML.
- ID - Возвращает или задает идентификатор для конкретного экземпляра класса Page. (Переопределяет Control.ID.)
- IdSeparator - Инфраструктура. Возвращает символ, используемый для разделения идентификаторов элемента управления при построении уникального идентификатора элемента управления на странице.
- IsAsync - Возвращает значение, определяющее, обрабатывается ли страница асинхронно.
- IsCallback - олучает значение, указывающее, является ли запрос страницы результатом обратного вызова.
- IsChildControlStateCleared - Возвращает значение, которое указывает, имеют ли элементы управления в этом элементе управления состояние элемента управления.
- IsCrossPagePostBack - Возвращает значение, указывающее, вовлечена ли страница в межстраничные обратные передачи.
- IsPostBack - Получает значение, указывающее, отрисовывается ли страница в первый раз или же загружается в ответ на обратную передачу.
- IsPostBackEventControlRegistered - Возвращает значение, указывающее, зарегистрирован ли страничный элемент управления, выполняющий обратную передачу.
- IsReusable - Инфраструктура. Возвращает значение, показывающее, может ли объект Page быть повторно используемым.
- IsTrackingViewState - Возвращает значение, отражающее сохранение изменений в состояние представления серверного элемента управления.
- IsValid - Возвращает значение, указывающее, завершена ли проверка страницы успешно.
- IsViewStateEnabled - Возвращает значение, показывающее, используется ли состояние представления для этого элемента управления.
- Items - Возвращает список объектов, хранящихся в контексте страницы.
- LCID - Инфраструктура. Устанавливает идентификатор языкового стандарта объекта Thread, связанного со страницей.
- LoadViewStateByID - Возвращает значение, указывающее, участвует ли элемент управления в загрузке состояния представления ID вместо индекса.
- MaintainScrollPositionOnPostBack - Возвращает или задает значение, указывающее, возвращается ли пользователь на прежнюю позицию в браузере после обратной передачи. Это свойство заменяет устаревшее свойство SmartNavigation.
- Master - Возвращает главную страницу, определяющую общий вид таблицы.
- MasterLocation - Получает или задает путь к главному представлению.
- MasterPageFile - Получает или задает виртуальный путь главной страницы.
- MaxPageStateFieldLength - Возвращает или задает максимальную длину поля состояния страницы.
- MetaDescription - Получает или задает содержимое элемента meta свойства "description".
- MetaKeywords - Получает или задает содержимое meta-элемента "keywords".
- Model - Получает свойство Model связанного объекта ViewDataDictionary.
- NamingContainer - Возвращает ссылку на контейнер именования элемента управления, создающий уникальное пространство имен для различения серверных элементов управления с одинаковыми значениями свойства Control.ID.
- Page - Возвращает ссылку на экземпляр Page, содержащий серверный элемент управления.
- PageAdapter - Возвращает адаптер, отображающий страницу для указанного запрашивающего браузера.
- PageStatePersister - Возвращает объект PageStatePersister, связанный со страницей.
- Parent - Возвращает ссылку на родительский элемент управления серверного элемента управления в иерархии элементов управления страницы.
- PreviousPage - Возвращает страницу, передавшую управление текущей странице.
- RenderingCompatibility - Получает значение, которое задает версию ASP.NET, с которой совместим созданный HTML.
- Request - Возвращает объект HttpRequest для запрашиваемой страницы.
- Response - Возвращает мелкое изображение HttpResponse, связанное с объектом Page. Этот объект позволяет отправить клиенту ответные данные HTTP и содержит сведения об этом ответе.
- ResponseEncoding - Инфраструктура. Устанавливает язык шифрования для текущего объекта HttpResponse.
- RouteData - Получает значение свойства RequestContext.RouteData текущего экземпляра System.Web.Routing.RequestContext.
- Server - Возвращает объект Server, являющийся экземпляром класса HttpServerUtility.
- Session - Возвращает текущий объект Session, предоставленный ASP.NET.
- Site - Возвращает сведения о контейнере, который содержит текущий элемент управления при визуализации на поверхности конструктора.
- SkinID - Получает или задает обложку, применимую к элементу управления.
- StyleSheetTheme - Получает или задает имя темы, применяемой к странице на ранних этапах жизненного цикла страницы.
- SupportAutoEvents - Инфраструктура. Возвращает значение, указывающее поддерживает ли элемент управления автоматические события TemplateControl.
- TempData - Получает временные данные для передачи в представление.
- TemplateControl - Возвращает или устанавливает ссылку на шаблон, содержащий элемент управления.
- TemplateSourceDirectory - Возвращает виртуальную папку Page или UserControl, содержащую текущий серверный элемент управления.
- Theme - Возвращает или задает тему страницы.
- Title - Возвращает или задает заголовок страницы.
- Trace - Возвращает объект TraceContext для текущего веб-запроса.
- TraceEnabled - Инфраструктура. Устанавливает значение, указывающее, разрешена ли трассировка для объекта Page.
- TraceModeValue - Инфраструктура. Устанавливает режим отображения операторов трассировке на странице.
- TransactionMode - Инфраструктура. Устанавливает уровень поддержки транзакций на странице.
- UICulture - Инфраструктура. Устанавливает идентификатор интерфейса пользователя (UI) для объекта Thread, связанного со страницей.
- UniqueFilePathSuffix - Возвращает уникальный суффикс для добавления к пути файла для браузеров с кэшем.
- UniqueID - Возвращает уникальный идентификатор серверного элемента управления в иерархии.
- Url - Получает или задает URL-адрес отображаемой страницы.
- User - Возвращает сведения о пользователе, производящем запрос страницы.
- Validators - Возвращает коллекцию всех проверочных элементов управления, содержащихся на запрашиваемой странице.
- ViewBag - Получает пакет представления.
- ViewContext - Получает или задает сведения, используемые для визуализации представления.
- ViewData - Получает или задает словарь, содержащий данные для передачи между контроллером и представлением.
- ViewState - Получает словарь сведений о состоянии, позволяющих сохранять и восстанавливать состояние представления серверного элемента управления при нескольких запросах одной и той же страницы.
- ViewStateEncryptionMode - Возвращает или задает режим шифрования состояния представления.
- ViewStateIgnoresCase - Возвращает значение, определяющее, является ли объект StateBag нечувствительным к регистру.
- ViewStateMode - Получает или задает режим состояния представления данного элемента управления.
- ViewStateUserKey - Присваивает пользователю идентификатор в переменной состояния представления, связанной с текущей страницей.
- Visible - Возвращает или задает значение, определяющее, отображается ли объект Page.
- Writer - Получает модуль записи текста, используемый для визуализации представления в ответ.
Все эти параметры работы MVC-странички можно увидеть в отладчике:
Ну а можно поставить пакет Glimpse - которое вытащит все эти параметры прямо в браузер и покажет рядом с FireBug.
На этом теоретическое введение в технологию MVC закончим и вернемся к коду. Небольшое продолжение теоретической части вы найдете в моей заметке - Общий шаблон простейшего приложения на ASP NET MVC.
Итак, в простейшем виде моя страничка со списком заработала, Теперь надо добавить пейджинг, реальную верcтку, параметры URL для пейджинга, правила роутинга для пейджинга.
В ASP.NET вестка этого сайта у меня выглядела вот так:
1: <div style="height: 10px"></div>
2: <asp:DataList ID="DataList1" runat="server" RepeatColumns="3" CellSpacing="10">
3: <ItemTemplate>
4: <table border="1" cellpadding="5px" cellspacing="0px">
5: <tr>
6: <td>
7: <asp:ImageButton ID="ImageButton1" runat="server" OnClick="ImageButton1_Click" /><br />
8: </td>
9: </tr>
10: <tr>
11: <td>
12: <asp:LinkButton ID="LinkButton1" Font-Size="Small" runat="server" OnClick="LinkButton1_Click">LinkButton</asp:LinkButton>
13: </td>
14: </tr>
15: </table>
16:
17: </ItemTemplate>
18: </asp:DataList>
19: <asp:Literal ID="Literal1" runat="server"></asp:Literal>
20: <div style="height: 10px"></div>
А весь код этой странички был такой:
1:
2: Sub FillCurrentPage()
3: 'текущая отображаемая пейджером страничка данных
4: Dim DVC As Data.DataView = MobileOperatorCount.Select(New DataSourceSelectArguments)
5: Dim Count1 As Integer = DVC(0)("Count")
6: 'заполнили пейджер
7: Dim MyPager As New NewPager
8: Literal1.Text = MyPager.GetHtmlTag(Count1, MobileOperatorPage.SelectParameters("PageNum").DefaultValue)
9: 'и собственно запрос за страничкой данных
10: Dim DV1 As Data.DataView = MobileOperatorPage.Select(New DataSourceSelectArguments)
11: DataList1.DataSource = DV1
12: DataList1.DataBind()
13: End Sub
14:
15: Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
16: If e.Item.DataItem IsNot Nothing Then
17: Dim ImageButton1 As ImageButton = CType(e.Item.FindControl("ImageButton1"), ImageButton)
18: ImageButton1.CommandName = CType(e.Item.DataItem, Data.DataRowView)("ID")
19: Dim LinkButton1 As LinkButton = CType(e.Item.FindControl("LinkButton1"), LinkButton)
20: LinkButton1.Text = CType(e.Item.DataItem, Data.DataRowView)("name")
21: LinkButton1.CommandName = CType(e.Item.DataItem, Data.DataRowView)("ID")
22: ImageButton1.ImageUrl = Go.BaseURL & "WebClient/interface/default/images/" & CType(e.Item.DataItem, Data.DataRowView)("image")
23: End If
24: End Sub
В MVC у меня получилась вот такая верстка (для самого убогого из возможных пейджера - лишь бы работало). Особенности синтаксиса RAZOR вы можете посмотреть здесь.
1: @ModelType IEnumerable(Of Terminal_MVC.GetOperatorPageResult)
2: @Code
3: Layout = "~/Views/Shared/M1.vbhtml"
4: End Code
5: <center><h3>Выберите оператора</h3></center>
6: <table cellspacing="10">
7: @For row As Integer = 0 To ViewData("PageSize") / 3 - 1
8: @<tr>
9: @Code
10: Dim ItemIndex1 As Integer = row * 3
11: Dim ItemIndex2 As Integer = row * 3 + 1
12: Dim ItemIndex3 As Integer = row * 3 + 2
13: End Code
14: <td>
15: @If ItemIndex1 <= Model(.Count - 1 Then
16: @<a href="@Url.Action("SetOperator", New With {.id = Model(ItemIndex1).ID})" >
17: <img src="@Href("~/WebClient/interface/default/images/" & Model(ItemIndex1).image)" alt="@Model(ItemIndex1).name" style="border:0;" />
18: </a>
19: End If
20: </td>
21: <td>
22: @If ItemIndex2 <= Model(.Count - 1 Then
23: @<a href="@Url.Action("SetOperator",New With {.id = Model(ItemIndex2).ID})" >
24: <img src="@Href("~/WebClient/interface/default/images/" & Model(ItemIndex2).image)" alt="@Model(ItemIndex2).name" style="border:0;" />
25: </a>
26: End If
27: </td>
28: <td>
29: @If ItemIndex3 <= Model(.Count - 1 Then
30: @<a href="@Url.Action("SetOperator",New With {.id = Model(ItemIndex3).ID})" >
31: <img src="@Href("~/WebClient/interface/default/images/" & Model(ItemIndex3).image)" alt="@Model(ItemIndex3).name " style="border:0;" />
32: </a>
33: End If
34: </td>
35: </tr>
36: Next
37: </table>
38: <center>
39: <ul>
40: @For i = 0 To ViewData ("PagerCount") - 1
41: @<li style="display: inline; padding: 10px">@Html.ActionLink(i.ToString, "Pager", New With {.id = i})
42: </li>
43: Next
44: </ul>
45: </center>
Это конечно совершенно другой уровень программирования. На ASP.NET достаточно было тыкныть в Datalist и выбрать заполнение таблички - построчное или поколоночное. Люди, которые делали MVC - в глаза не видели ASP.NET. Проблемы не только в подходе, игнорирующем наработки последних восьми лет, но в самой Visual Studio. Проклятые микрософтовцы так и не добились чтобы программный код НЕ ПЕРЕФОРМАТИРОВАЛСЯ при открытии/закрытии странички в дизайнере. Открываешь/закрываешь HTML-шаблон странички - а там уже вместо твоего собственного кода оказываются перемешанные помои. Впрочем, шаблон странички в форме View открыть в дизайнере нельзя вообще!
Код этого фрагмента получается довольно простой (хотя и не настолько простой, как код ItemDataBound в ASP.NET):
1: Namespace Terminal_MVC
2: Public Class PaymentController
3: Inherits System.Web.Mvc.Controller
4:
5: Dim OperatorGroupID As Integer = 1
7: '
8: 'Для пейджера
9: Dim RowCount As Integer
10: Dim PageSize As Integer = 30
11: '
12: ' GET: /Payment
13: Function Index() As ActionResult
14: Return View()
15: End Function
16: '
17: ' POST: /Payment/SetBarcode
18: <HttpPost()>
19: Function SetBarcode(ByVal collection As FormCollection) As ActionResult
20: ViewData("Barcode") = collection("BarCode")
21: Return RedirectToAction("OperatorPage")
22: End Function
23: '
24: ' GET: /Payment/OperatorPage
25: Function OperatorPage() As ActionResult
26: Return RefreshPage(1)
27: End Function
28: '
29: ' GET: /Payment/Pager/1
30: Function Pager(ByVal id As Integer) As ActionResult
31: Return RefreshPage(id)
32: End Function
33: '
34: 'рефреш заданной странички
35: Function RefreshPage(ByVal id As String) As ActionResult
36: GetRowCount()
37: ViewData("PagerCount") = RowCount \ PageSize + 1
38: ViewData("PageSize") = PageSize
39: Return View("OperatorPage", GetOnePage(id))
40: End Function
41: '
42: 'считать общее количество записей
43: Function GetRowCount() As Integer
44: Dim SqlDataContext As New MobileOperatorDataContext()
45: RowCount = SqlDataContext.ExecuteQuery(Of Integer)("select count(*) as [Count] from GISIS.dbo.MobileOperator where GroupID=" & OperatorGroupID.ToString)(0)
46: End Function
47: '
48: 'Считать текущую страничку
49: Function GetOnePage(ByVal PageNum As Integer)
50: Dim dataContext As New MobileOperatorDataContext
51: Return dataContext.GetOperatorPage(OperatorGroupID, PageSize, PageNum).ToList()
52: End Function
53:
54: ' GET: /Payment/SetOpetator/1
55: Function SetOperator(ByVal id As Integer) As ActionResult
56: Return View()
57: End Function
58:
59: End Class
60: End Namespace
Я уже писал в топике Знакомство с Visual Studio 2010 что один из радикальных глюков Билогейтсовских индусов при разработке Visual Studio был в том, что они не сделали нормального отладчика в Visual Studio. В сущности это не столько глюк идеологии MVC, сколько Visual Studio, которая является инструментом программирования для MVC. В MVC все закручено на Лямбда-выражениях, анонимных функциях и LINQ - а отладчика-то для них и нет !!!
Второй адский глюк Билогейтсовских индусов в том, что код контроллера нельзя менять на ходу !!! Любой ASP.NET-программист будет просто в шоке, узнав это. В это трудно поверить, тем не менее это так. То есть надо остановить web-сервер, исправить одну буковку и перезапустить все заново! Похоже PHP-шные уроды, которых нанял Билл Гейтс в каких-то курятниках и свинарниках Бомбея - понятия не имеют об ASP.NET, не видели как это работает, не понимают чего ждут программисты в качестве развития ASP.NET. Эти PHP-шные уроды никогда не слышали о фоновой компиляции и тупо предлагают нам взамен ASP.NET среду программирования, которая у нас была 20 лет назад в Visual InterDev .
Но, тем не менее, рожаем мозгом этот код и верстку, тыкаем чтобы проверить - все работает!
Кое каких рисунков у меня не хвататает в каталоге WebClient - но это уже не важно. Пейджер тоже можно украсить получше. А пока можно двигаться дальше и делать следующую форму.
Следующая форма у меня будет крученая как в плане кода, так и в плане верстки. Мой юзер выбрал оператора услуг - этому оператору услуг соответствует файлик с параметрами, которые надо принять у юзера.
1: <operator id="890" cyber_id="890" group_id="8">
2: <name>Госпошлины ГИБДД</name>
3: <name_for_cheque>УВД ГИББД</name_for_cheque>
4: <inn_for_cheque />
5: <limit min="50" max="15000" />
6: <fields>
7: <field id="104" type="enum">
8: <name>Тип госпошлины</name>
9: <enum>
10: <item value="Регистрация ТС">Регистрация ТС</item>
11: <item value="Снятие ТС с учета">Снятие ТС с учета</item>
12: <item value="Выдача транзитных номеров">Выдача транзитных номеров</item>
13: <item value="Повторная выдача свидетельства">Повторная выдача свидетельства</item>
14: <item value="Внесение изменений в ПТС">Внесение изменений в ПТС</item>
15: <item value="ПТС">ПТС</item>
16: <item value="Регистрация мотоцикла">Регистрация мотоцикла</item>
17: <item value="Регистрация прицепа">Регистрация прицепа</item>
18: <item value="Техосмотр">Техосмотр</item>
19: <item value="Замена гос номера">Замена гос номера</item>
20: <item value="Передача по наследству">Передача по наследству</item>
21: </enum>
22: </field>
23: <field id="100" type="text">
24: <name>Фамилия Имя Отчество</name>
25: </field>
26: <field id="101" type="integer" minlength="10" maxlength="18">
27: <name>ОКАТО подразделения ГИБДД</name>
28: <comment>Введите код ОКАТО подразделения ГИБДД по месту прописки</comment>
29: </field>
30: <field id="106" type="masked">
31: <name>ИНН подразделения ГИБДД</name>
32: <mask>**********</mask>
33: <comment>Введите ИНН подразделения ГИБДД по месту прописки</comment>
34: </field>
35: <field id="102" type="text">
36: <name>Адрес плательщика</name>
37: </field>
38: <field id="103" type="masked">
39: <name>Номер телефона</name>
40: <mask>8 (***) ***-**-**</mask>
41: <comment>[b]Внимание![/b] Номер телефона вводится без "[b]8[/b]".</comment>
42: </field>
43: </fields>
44: <processor offline="1">
45: <check amount-value="10">
46: <url>http://proc.starpay.ru:10036/cgi-bin/gb/gb_pay_check.cgi</url>
47: <request-property name="NUMBER" field-id="100" />
48: <request-property name="ACCOUNT">
49: <fixed-text id="31||" />
50: <field-ref id="101" />
51: </request-property>
52: <request-property name="PURPOSE" field-id="104" />
53: <request-property name="ADDRESS" field-id="102" />
54: <request-property name="AGREE">
55: <fixed-text id="1" />
56: </request-property>
57: <request-property name="INN" field-id="106" />
58: </check>
59: <payment>
60: <url>http://proc.starpay.ru:10036/cgi-bin/gb/gb_pay.cgi</url>
61: <request-property name="NUMBER" field-id="100" />
62: <request-property name="ACCOUNT">
63: <fixed-text id="31||" />
64: <field-ref id="101" />
65: </request-property>
66: <request-property name="PURPOSE" field-id="104" />
67: <request-property name="ADDRESS" field-id="102" />
68: <request-property name="AGREE">
69: <fixed-text id="1" />
70: </request-property>
71: <request-property name="INN" field-id="106" />
72: </payment>
73: <status>
74: <url>http://proc.starpay.ru:10036/cgi-bin/gb/gb_pay_status.cgi</url>
75: </status>
76: </processor>
77: <commission>
78: <part value="50" />
79: <part min="1001" value="5%" />
80: </commission>
81: <image>ohr_gibdd.gif</image>
82: </operator>
Этих параметров может быть произвольное число и они бывают разных типов Enum, Int, Masked, Text - кроме того, в этом файлике определен специальный порядок упаковки результата в итоговое поле, которое будет отправлено на оплату через Cyberplat.
Распарсить нерегулярный XML в регулярную структуру - это нетривиальная задача, но в этом топике, посвященном MVC - я на этом останавливаться не буду. Для дальнейшего повестования об MVC существенно лишь то, что мой класс порождает коллекцию Fields As New Collections.Generic.List(Of OneCyberField), в которой есть все нужные поля, чтобы скормить эту модель шаблону странички.
Cоздаем шаблон странички (View) как и в предыдущих случаях:
Модель здесь более сложная, формируемая на основе коллекции из четырех разных классов CyberEnum, CyberInt, CyberMasked, CyberText (которые основаны на общем интерфейсе CyberCommonFields). Поэтому для начала я ввожу червновой код и проверяю как раскрывается моя хитрая модель.
Работает отлично, не особо заморачиваясь на дизайне (и не имея в данный момент времени на валидацию полей) по быстренькому делаю вот такую верстку:
1: @ModelType Collections.Generic.List(Of Terminal_MVC.OneCyberField)
2: @Code
3: Layout = "~/Views/Shared/M1.vbhtml"
4: End Code
5:
6: <center><h3>Введите параметры платежа</h3></center>
7:
8: <script type="text/javascript" language="javascript">
9: function save1(hiddenfield,storevalue)
10: {
11: var x = document.getElementById(hiddenfield);
12: x.value = storevalue;
13: }
14: </script>
15:
16: @Using Html.BeginForm("SetPaymentFields", "Payment")
17:
18: @<table>
19: @For Each One In Model
20:
21: Select Case One.FieldType
22: Case Terminal_MVC.CyberFieldsType._Enum_
23:
24: @<tr>
25: <td><b>@CType(One.FieldDefinition, Terminal_MVC.CyberEnum).Name</b>
26: </td>
27: <td>
28: <input type="hidden" id="@CType(One.FieldDefinition, Terminal_MVC.CyberEnum).ID" name="@CType(One.FieldDefinition, Terminal_MVC.CyberEnum).ID" />
29: <select onchange='save1("@CType(One.FieldDefinition, Terminal_MVC.CyberEnum).ID",this.options[selectedIndex].value);'>
30: @For Each OneListItem In CType(One.FieldDefinition, Terminal_MVC.CyberEnum).EnumFields
31: @<option id="@OneListItem.Value" value="@OneListItem.Value">@OneListItem.Text</option>
32: Next
33: </select>
34: </td>
35: </tr>
36:
37: Case Terminal_MVC.CyberFieldsType._Int_
38:
39: @<tr>
40: <td><b>>@CType(One.FieldDefinition, Terminal_MVC.CyberInt).Name</b>
41: </td>
42: <td>@Html.TextBox(CType(One.FieldDefinition, Terminal_MVC.CyberInt).ID)
43: </td>
44: </tr>
45: @<tr>
46: <td colspan="2">@CType(One.FieldDefinition, Terminal_MVC.CyberInt).Comment ( @CType(One.FieldDefinition, Terminal_MVC.CyberInt).MinLength - @CType(One.FieldDefinition, Terminal_MVC.CyberInt).MaxLength знаков )
47: </td>
48: </tr>
49:
50:
51: Case Terminal_MVC.CyberFieldsType._Masked_
52:
53:
54: @<tr>
55: <td><b>@CType(One.FieldDefinition, Terminal_MVC.CyberMasked).Name</b>
56: </td>
57: <td>@Html.TextBox(CType(One.FieldDefinition, Terminal_MVC.CyberMasked).ID)
58: </td>
59: </tr>
60: @<tr>
61: <td colspan="2">@CType(One.FieldDefinition, Terminal_MVC.CyberMasked).Comment ( @CType(One.FieldDefinition, Terminal_MVC.CyberMasked).Mask )
62: </td>
63: </tr>
64:
65:
66: Case Terminal_MVC.CyberFieldsType._Text_
67:
68: @<tr>
69: <td><b>@CType(One.FieldDefinition, Terminal_MVC.CyberText).Name</b>
70: </td>
71: <td>@Html.TextBox(CType(One.FieldDefinition, Terminal_MVC.CyberText).ID)
72: </td>
73: </tr>
74: @<tr>
75: <td colspan="2">@CType(One.FieldDefinition, Terminal_MVC.CyberText).Comment
76: </td>
77: </tr>
78:
79:
80: End Select
81:
82: @<tr>
83: <td colspan="2" style="height: 5px;">
84: </td>
85: </tr>
86: @<tr>
87: <td colspan="2" style="background-color: Gray; height: 1px;">
88: </td>
89: </tr>
90: @<tr>
91: <td colspan="2" style="height: 10px;">
92: </td>
93: </tr>
94: Next
95: </table>
96:
97: @<input type="submit" value="OK" />
98:
99: End Using
Проверяю - как видите все работает, параметры постбека приходят нормально. Дальше можно переходить к следующей форме.
Думаю, принцип построения последующих форм понятен. Набор хелперов настолько ограничен, что даже простой Select/Option на форме выше мне пришлось сделать самому. Это напоминает не просто программирование в Нотепаде, это похоже на программирование на ассемблере. Почему умер простой ASP когда появился ASP.NET? Он имел существенно меньшую скорость разработки, чем ASP.NET - и как на простом ASP можно было зарабатывать деньги, когда заказчика интересует результат, а не название технологической платформы? А MVC это в чистом виде простой ASP, только с добавление обьектного экстремизма и дополнительных чемоданов знаний (типа LINQ), которые ничем не помогают, но которые надо таскать за собой. Хм, не знаю насколько это жизнеспособно при работе в режиме фрилансера, когда деньги приносит только скорость работы.
Я не ставлю тут задачу полного описания работы этого моего сайта - поэтому я опустил наиболее существенные смысловые алгоритмы упаковки собранных от юзера данных в формат киберплата, алгоритмы парсинга этого XML на LINQ, алгоритмы создания хитрой коллекции из разных классов, наследуемых от одного интерфейса, как последняя версия этого XML попала в мою базу из Киберплата, как делается биллинг и прочие важные алгоритмы этого сайта.
Мне просто хотелось сделать (в том числе для себя) некоторое сравнение подходов ASP.NET и MVC, а заодно сделать step-by-step инструкцию для тех, кто стоит на еще более ранней стадии знакомства с MVC и обдумать - есть ли смысл переходить с ASP.NET на MVC? А может проще перейти с ASP.NET на Flex? Для себя (по состоянию на конец 2010 года) я этого решения еще не принял. Вроде бы в MVC технологических возможностей побольше, сайт конвертируется в PHP, но скорость разработки снижается раз в десять наверное. А самые элементарные вещи, типа разворота данных в табличке вместо строк по столбцам (которые выполнялись в Datalist ASP.NET ровно в один клик мышкой) теперь становятся проблемой. Кроме того, для версток в MVC теперь Visual Studio недостаточно - нужны другие инструменты.
Думаю, описание остальных форм моего первого сайта на MVC уже ничего не добавят к заявленным целям этой странички и есть смысл на этих трех первых формах остановиться. Модели в этих первых трех формах у меня получились все разные - просто класс с одним полем, стандартная коллекция и SQL/LINQ-коллекция. Это будет полезный и общедоступный сквозной пример. В заключение я скажу пару слов о валидации (которую я прикрутил к этому моему сайту чуть позже) - таким образом на одной страничке будет сосредоточено описание не только трех моделей, но и трех видов постбеков странички к серверу - GET,POST и асинхронного.
Итак, в ASP.NET у нас было семь стандартных валидаторов, среди которых Custom Validator c серверной функцией валидации. Ставился он беспредельно просто - одним единственным drag-and-drop'ом - после чего указывалось в атрибуте OnServerValidate имя серверной функции валидации - и все! Функция валидации тупо писалась прямо в теле странички как ни в чем не бывало. Всю остальную черновую работу брала на себя среда ASP.NET (кстати в этом же контроле можно было указывать в ClientValidationFunction и клиентскую функцию валидации).
Для этой формы мне потребовался такой же точно валидатор. Понятно что для клиента такие расчеты как проверка корректности телефона по маске, ИНН или ОКПО будет нереально тяжеловесна (одних запросов в базу сколько тут нужно). Поэтому мне нужна была именно стандартная серверная валидация (подобная классической ASP.NET валидации) - которая бы отправляла асинхронный постбек к серверу и возвращала бы страничке результат валидации.
В MVC мне удалось решить эту задачку на JQUERY (есть и альтернативный путь - микрософтовскими AJAX-скриптами). Это решение я предлагаю вашему вниманию (и надеюсь вы можете на этом примере асазнать пропасть между классическим ASP.NET и ASP.NET MVC):
На форме я указал вот такие строки:
<input type="text" id="@CType(One.FieldDefinition, Terminal_MVC.CyberInt).ID" onchange="validate1(this.id,this.value);" />
В терминах обьектного экстремизма эту же мысль для первой формы можно выразить вот так (а для третьей формы я даже затрудняюсь сформулировать это выражение в виде анонимной функции расширения):
@Html.TextBoxFor((Function(model) model.BarCode), New With {.onchange = "validate1(this.id,this.value);"})
После этого мне пришлось отладить вызов JQUERY и прием JSON с сервера:
1: <script type="text/javascript" language="javascript">
2: function validate1 (id,value) {
3: var x = document.getElementById(id);
4: $.getJSON("/Payment/Validate",
5: { id: x.id, value: x.value },
6: ret1);
7: }
8:
9: function ret1(json,success) {
10: var x = document.getElementById(json.id);
11: x.style.color = json.color;
12: }
13: </script>
Естественно сама JQUERY присутствует в HEAD мастер-странички (Layout-странички в новой индусской терминологии):
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
После этого мне пришлось в контроллере принять JSON, собранный с формы JQUERY-функций getJSON, сформулировать ответ контроллера в виде объекта JsonResult - и лишь после полной отладки этого враппера, я смог в теле функции Validate собственно приступить к смысловому алгоритму валидации (к тому самому алгоритму, который я начинал писать на ASP.NET через полсекунды после драг-анд-дропа кастом-валидатора на форму) :
1: ' GET: /Payment/Validate
2: Function Validate(ByVal ID As String, ByVal Value As String) As JsonResult
3: Dim Result As New JsonResult
4: '
5: 'тут собственно алгоритм валидации, возвращающий "" или "red" в случае неверного вводимого символа
6: '
7: Result.Data = New With {.id = ID, .color = "red"}
8: Result.JsonRequestBehavior = Mvc.JsonRequestBehavior.AllowGet
9: Return Result
10: End Function
Еще несколько поучительных примеров использования jQuery я описал на страничках Как с помощью jQuery сделать флеш-ролик резиновым, AJAX подсказка/автозаполнение на jQuery, Как сделать простейший Web-handler - формирующий XML или JSON.
В заключение я бы хотел добавить сравнительный анализ MVC и ASP.NET из книги знаменитого микрософтовского пропагандиста Гайдара Магданурова (с которым я во многом согласен):
Преимущества WebForms
- Высокая скорость разработки
- Большое количество готовых компонентов
- Богатая поддержка средствами разработки
- Автоматическое управление состоянием
- Событийная модель управления состоянием
- Высокая степень абстракций над HTML-разметкой
- Простота изучения технологии
- Возраст технологии
- Связка разметки и логики страницы
- Отсутствие полного контрол над конечной разметкой страницы
- Сложность тестирование логики приложения
- Неестественная для Web-среды модель сохранения состояния
- Четкое разделение логических слоев
- Полный контроль над разметкой
- Логическое разделение функциоанльности
- Красивые URL адреса
- Прозрачный процесс обработки запроса
- Поддержка различных движков генерации разметки
- Полноценная поддержка всех возможностей платформы ASP.NET
- Более высокий порог входа в технологию
- Отсутсвие механизма хранения состояния
- Сложности создания библиотек компонентов
- Молодость технологии
Набор требований для выбора между технологиями MVC и ASP.NET:
WebForms | MVC | |
- | + | Полный контроль над HTML-разметкой |
+ | +/- | Поддержка визуального редактора страниц |
+/- | + | Возможность автоматического тестирования логики приложения |
- | + | Возможность замены движка генерации разметки |
+ | +/- | Высокая скорость разработки |
+/- | + | Простота поддержки при необходимости частого внесения изменения |
+/- | + | Небольшая команда разработчкиков |
+ | - | Использование готовых элементов управления |
+/- | + | Простота поисковой оптимизации готового решения |
+ | +/- | Приложение делается только для локальной сети |
+ | - | Необходимость быстрее разработать прототип для проверки идеи |
+ | - | Участие разработчиков, знакомых только с Windows-технологиями |
+/- | + | Богатый скриптовый функционал на стороне клиента |
+ | - | Вэб-приложение является интерфейсом для базы данных |
- | + | Приложение может быть сконвертировано в PHP |
После написания моего первого сайта на MVC, мое мнение об MVC пожалуй не изменилось. Как-то на MVC конечно программировать возможно, но хочется закончить эту страничку с введением в технологию MVC ровно так же, как закончил страничку о Visual Studio 2010:
Адское сырье нам подсунули опять из MS под видом "пиридавой технологии". Где так необходимое и востребованное развитие ASP.NET? Где новые комплекты контролов? Возможности работать с фреймами, возможности нескольких тегов FORMS, полноценнй интрументарий изготовления контролов, которые можно было бы ложить в библиотеки и использовать во многих проектах? Нормальные инструменты вместо EVENTTARGET, позволяющие видеть еще в Page_Load от кого идет постбек. Думаю, каждый практикующий ASP.NET программист перечислил бы десятки позиций, которых ему не хватает для комфортной работы в ASP.NET. Если бы эти инструменты были бы реализованы, то технология ASP.NET заняла долю не 0,4% рынка web-приложений, а стала бы полностью конкурентной.
А меньше всего в развитии ASP.NET требовался ASP.NET URLRewriter, тем более он существует во множестве автономных OpenSource пакетов, свободно догружаемых в ASP.NET (и многие их использовали начиная с ASP.NET 1.1). A еще меньше требовалось полное исключение контролов - технология ASP.NET прекрасно позволяла работать и без них - просто по литералам (у меня множество страниц сделано совершенно без контролов). Ожидаемого и востребованного развития ASP.NET не произошло - вместо этого с огромной помпой подсовывают какое адское сырье.
От всего этого сырья даже до отсталого PHP - ровно как до Луны раком. Только когда в этой "пиридавой тихналогии" появятся ДЕСЯТКИ CMS хотя бы такого качества как JOOMLA и DRUPAL (вот первый попавшийся список из 237 PHP OpenSource CMS) - и когда в каждой из этих CMS появятся по несколько тысяч готовых бесплатных модулей (хотя бы такого качества и функционала как vluemart - где содержится полный готовый электронный магазин) - вот с этого момента (а не раньше) это чудо-пиридавая технология от билла-де-билла хоть как-то сможет вступить в конкуренцию с "отсталыми" технологиями - по цене среды девелопмента, по цене хостинга, по количеству багов в IDE, по количество плагинов в CMS, по цене операционной системы для хостинга сайта, по скорости освоения технологической платформы, по кроссплатформенности технологии, по удобству и бесплатности доступа к дополнительным модулям CMS, по функциональности этих модулей и тд и тд..
Жаль в общем-то, что MS всех кинула и прекратила развитие весьма-весьма неплохой технологии ASP.NET. Но раз уж вы попали на мою страничку об ASP.NET MVC - то я рекомендую вам почитать как делается верстка в ASP.NET MVC. Ну а тем, кому такое чтиво не асилить - можете почитать что-нибудь попроще - Подарки от Microsoft для ASP.NET программистов.
|