Мой первый сайт на MVC 3 Razor
![Visual Studio 2010](/MVC3_step_by_step/VS2010.png)
Возможно для меня лично сменить основную рабочую платформу хостинга с 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.
![](/MVC3_step_by_step/B1-.jpg)
![](/MVC3_step_by_step/B2-.jpg)
![](/MVC3_step_by_step/B3-.jpg)
![](/MVC3_step_by_step/B4-.jpg)
Далее я выполняю все предварительные косметические действия по настройке шаблонного проекта. Добавляю папочки WebClient и Images, в которой лежат нужные мне работы для сайта рисуночки, файлик со стилями проекта, убираю ненужную папку для MDF-файла, из которой умеет подхватывать базы SQLExpress, а вместо этого устанавливаю нормальную connectionstring к моей версии MS SQL.
Вставляю волшебные строчки, чтобы в этом проете могли работать ASHX-хандлеры и ASPX-странички. Я выбрал себе для работы имя контроллера Payment - поэтому его и впишу в имя дефолтного правила для обработки URL. После создания шаблона сайта его можно попробовать запустить - чтобы увидеть html-форму стандартного приложения.
![](/MVC3_step_by_step/D1-.jpg)
![](/MVC3_step_by_step/D2-.jpg)
![](/MVC3_step_by_step/B5-.jpg)
Весь цикл создания странички начинается с модели - для чего для своей первой странички я создаю модель в виде простейшего класса из одного поля. Далее надо обязательно первый раз откомпилировать приложение, чтобы появились классы в подсказке.
Далее смотрю, как в движке Razor стартовая страничка приложения запускает мастер_page и вписываю туда свою начальную страничку приложения.
![](/MVC3_step_by_step/E1-.jpg)
![](/MVC3_step_by_step/E2-.jpg)
![](/MVC3_step_by_step/E3-.jpg)
Создаю папку Payment для шаблонов страниц и добавляю туда шаблон страницы Index.vbhtml (после первой компиляции классы для подсказки уже есть). При создании стартовой странички указываю мастер-пейдж проекта M1.vbhtml - появляется первая шаблонная страничка, теперь надо создать шаблон Master-page, который был указан для этой странички. Это обычная html-страничка, в теле которой есть @RenderBody - в это место будут включено тело шаблона Index (и других).
![](/MVC3_step_by_step/F1-.jpg)
![](/MVC3_step_by_step/F2-.jpg)
![](/MVC3_step_by_step/F3-.jpg)
![](/MVC3_step_by_step/F4-.jpg)
![](/MVC3_step_by_step/F5-.jpg)
![](/MVC3_step_by_step/F6-.jpg)
![](/MVC3_step_by_step/F7-.jpg)
Теперь создаю собственно первую простейшую страничку проекта Paymetnt/index.vbhtml. Модель, по которой работает шаблон странички указана в директиве @ModelType, поэтому все работает с подсказкой.
![](/MVC3_step_by_step/G1-.jpg)
Теперь создаю контроллер для заполнения этого шаблона - в нем должно быть минимум Index (Page_load для начального заполнения странички) и указанный мною метод SetBarcode, который примет Postback от странички. Это будет начальное правило по умолчанию в моей таблице URL-маршритизации. Имя контроллера (в соответствии с правилом роутинга URL, прописанным в Global.asax) должно совпадать с именем папки с View - шаблонами страниц.
![](/MVC3_step_by_step/H1-.jpg)
![](/MVC3_step_by_step/H2-.jpg)
![](/MVC3_step_by_step/H3-.jpg)
Создаю код метода SetBarcode, в котором принимаю переменную отправленную с html-странички.
![](/MVC3_step_by_step/H4-.jpg)
Теперь можно попробовать запустить проект. Ура, первая страничка с заглушкой вместо модели работает - проверяю как контроллер ловит постбек.
![](/MVC3_step_by_step/I1-.jpg)
![](/MVC3_step_by_step/I2-.jpg)
![](/MVC3_step_by_step/I3-.jpg)
Постбек проходит нормально (дальше видно что следующей странички еще нет) - значит начинаем делать вторую страничку с более сложной моделью.
Для начала создаю модель, с которой будет работать мой шаблон SetBarcode. У меня есть SQL-база, по которой будет работать страничка.
![](/MVC3_step_by_step/J1-.jpg)
В ASP.NET MVC модели реляционных данных бывают двух типов - Entity framework и Linq to SQL. Entity framework считается последней, более сложной и универсальной версией. В этом проекте мне это совсем не нужно, зато нужна прямая поддержка XML. Поэтому решаю делать на более простом Linq to SQL.
![](/MVC3_step_by_step/L1-.jpg)
![](/MVC3_step_by_step/L2-.jpg)
![](/MVC3_step_by_step/L3-.jpg)
![](/MVC3_step_by_step/L4-.jpg)
У меня есть табличка MobileOperator, есть вьюшка, которая парсит XML в этой табличке и есть пейджинговая процедурка GetOperatorPage, которая выдает нужную страничку данных из этой процедурки. Создаю метод GetOperatorPage который должен выдавать обьекты типа GetOperator - на этом все, модель для изготовления шаблона (View) готова.
![](/MVC3_step_by_step/M1-.jpg)
![](/MVC3_step_by_step/M2-.jpg)
![](/MVC3_step_by_step/M3-.jpg)
Теперь создаю автоматически генерируемый шаблон странички OperatorPage для отображения списка результатов, выдаваемых процедуркой GetOperatorPage.
![](/MVC3_step_by_step/N1-.jpg)
![](/MVC3_step_by_step/N2-.jpg)
![](/MVC3_step_by_step/N3-.jpg)
![](/MVC3_step_by_step/N4-.jpg)
![](/MVC3_step_by_step/N5-.jpg)
В контроллере указываю новое View - куда будет переход после постбека и набор данных, который сгенерирован вызовом процедуры GetOperatorPage запускаю страничку - все как-то запускается и отображается.
![](/MVC3_step_by_step/N1-.jpg)
![](/MVC3_step_by_step/N2-.jpg)
![](/MVC3_step_by_step/N3-.jpg)
![](/MVC3_step_by_step/N4-.jpg)
![](/MVC3_step_by_step/N5-.jpg)
Это простейший вариант обработчика события. В принципе обработчик можно вернуть следующие результаты:
- 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-странички можно увидеть в отладчике:
![](/MVC3_step_by_step/PageProperty1_1.gif)
![](/MVC3_step_by_step/PageProperty2_1.gif)
Ну а можно поставить пакет 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), в которой есть все нужные поля, чтобы скормить эту модель шаблону странички.
![](/MVC3_step_by_step/Q1-.gif)
![](/MVC3_step_by_step/Q2-.gif)
Cоздаем шаблон странички (View) как и в предыдущих случаях:
![](/MVC3_step_by_step/R1-.gif)
![](/MVC3_step_by_step/R2-.gif)
![](/MVC3_step_by_step/R3-.gif)
![](/MVC3_step_by_step/R4-.gif)
Модель здесь более сложная, формируемая на основе коллекции из четырех разных классов CyberEnum, CyberInt, CyberMasked, CyberText (которые основаны на общем интерфейсе CyberCommonFields). Поэтому для начала я ввожу червновой код и проверяю как раскрывается моя хитрая модель.
![](/MVC3_step_by_step/R5-.gif)
![](/MVC3_step_by_step/R6-.gif)
Работает отлично, не особо заморачиваясь на дизайне (и не имея в данный момент времени на валидацию полей) по быстренькому делаю вот такую верстку:
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
Проверяю - как видите все работает, параметры постбека приходят нормально. Дальше можно переходить к следующей форме.
![](/MVC3_step_by_step/R7-.gif)
![](/MVC3_step_by_step/R8-.gif)
Думаю, принцип построения последующих форм понятен. Набор хелперов настолько ограничен, что даже простой 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 и клиентскую функцию валидации).
![](/MVC3_step_by_step/U1-.gif)
![](/MVC3_step_by_step/U2-.gif)
![](/MVC3_step_by_step/U3-.gif)
Для этой формы мне потребовался такой же точно валидатор. Понятно что для клиента такие расчеты как проверка корректности телефона по маске, ИНН или ОКПО будет нереально тяжеловесна (одних запросов в базу сколько тут нужно). Поэтому мне нужна была именно стандартная серверная валидация (подобная классической 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 программистов.
![](http://forum.vb-net.com/GetTopicCount.png?id=EA6BB5DF-D40E-4DC7-9773-5D0B8D1CDC12)
<SITEMAP> <MVC> <ASP> <NET> <DATA> <KIOSK> <FLEX> <SQL> <NOTES> <LINUX> <MONO> <FREEWARE> <DOCS> <ENG> <CHAT ME> <ABOUT ME> < THANKS ME> |