В одном месте поменял, в другом забыл – получил баг. У меня был такой опыт: изменил перечисление на сервере, на клиенте забыл. В итоге логика работы кнопок иногда стала подглючивать, только при определенной комбинации фильтров и т.д. Ошибку обнаружили тестировщики, через пару дней проверок, к тому времени я конечно уже давно забыл, что я менял перечисление и зачем я это делал. В итоге, чтобы локализовать ошибку, мне пришлось потратить больше часа времени...
Чтобы избавиться от такого рода ошибок и от лишней работы, я использую T4-преобразования, и генерирую JS-файлы на основе C#. В этой статье я расскажу, как такие преобразования устроены и работают. В конце статьи выложен код итогового T4-преобразования, которое можно скачать и использовать в ваших собственных проектах без изменений. Итак, поехали!
Code Model
Code Model – это функциональность, предоставляемая Visual Studio, позволяющая просматривать (и даже изменять) описания классов, методов, свойств и полей во всем вашем проекте. Похоже на Reflection, но намного удобнее. Подробнее о Code Model можно прочитать на MSDN.Замечание: Code Model работает только в среде Visual Studio, поэтому использующие этот функционал T4-преобразования нельзя запускать за пределами VS, например в Continious Integration-билдах.
Code Model позволяет перебирать все элементы кода, и у каждого из этих элементов есть коллекция Children, которую тоже можно перебрать, и т.д.
Простейший пример использования Code Model выглядит примерно так:
foreach (EnvDTE.CodeElement element in projectItem.FileCodeModel.CodeElements) { if (element.Kind == EnvDTE.vsCMElement.vsCMElementNamespace) { foreach (EnvDTE.CodeElement innerElement in element.Children) { switch (innerElement.Kind) { case EnvDTE.vsCMElement.vsCMElementClass: Debug.WriteLine("Class: " + innerElement.Name); break; case EnvDTE.vsCMElement.vsCMElementEnum: Debug.WriteLine("Enum: " + innerElement.Name); break; case EnvDTE.vsCMElement.vsCMElementInterface: Debug.WriteLine("Interface: " + innerElement.Name); break; } } } }
Соответственно, если взглянуть дальше вглубь классов/перечислений/интерфейсов, сгенерировать их описания для JavaScript довольно легко.
Кстати, очень хороший пример с генерацией SQL-таблиц на основе C#-перечислений с использованием T4 и Code Model можно найти у Олега Сыча. У Олега в блоге вообще очень много про T4, рекомендую почитать.
ООП и ASP.Net Ajax
Для реализации ООП в JavaScript я использую в своих проектах библиотеку ASP.Net Ajax. К слову, эта библиотека (версии 3.5) уже присутствует в составе SharePoint, так что SharePoint-разработчикам ничего дополнительно подключать не потребуется.Как известно, JavaScript не является объектно-ориентированным языком, поэтому ООП-объекты JavaScript выглядят порой довольно неказисто. Например, чтобы объявить перечисление в ASP.Net Ajax, при этом используя inline-документацию, нужно написать довольно много кода. Реальный пример из рабочего проекта:
DW.TasksDashboard.DashboardFilter = function() { /// <summary>Предустановленные фильтры для Центра задач</summary> /// <field name='AllTasks' type='Number' integer='true' static='true'>Все задачи (без фильтрации)</field> /// <field name='TasksAssignedToMe' type='Number' integer='true' static='true'>Задачи, назначенные на текущего пользователя</field> /// <field name='TasksCreatedByMe' type='Number' integer='true' static='true'>Задачи, созданные текущим пользователем</field> /// <field name='ManagerTasks' type='Number' integer='true' static='true'>Задачи, созданные руководителем текущего пользователя и назначенные на текущего пользователя</field> /// <field name='DepartmentTasks' type='Number' integer='true' static='true'>Задачи, назначенные на членов отдела текущего пользователя (включая его самого)</field> /// <field name='DeputyTasks' type='Number' integer='true' static='true'>Задачи, назначенные на пользователей, которых замещает текущий пользователь</field> /// <field name='SecretaryTasks' type='Number' integer='true' static='true'>Задачи, назначенные на пользователей, для которых текущий пользователь - секретарь</field>} DW.TasksDashboard.DashboardFilter.prototype = { AllTasks: 1, TasksAssignedToMe: 2, TasksCreatedByMe: 3, ManagerTasks: 4, DepartmentTasks: 5, DeputyTasks: 6, SecretaryTasks: 7 } DW.TasksDashboard.DashboardFilter.registerEnum('DW.TasksDashboard.DashboardFilter');
Как описывать другие ООП-объекты с помощью ASP.Net Ajax (классы, интерфейсы, события и т.д.), вы можете найти в документации по этой библиотеке, например в статье Extending JavaScript with ASP.NET AJAX. Имея это знание, написать итоговый код несложно. Взамен, вы можете использовать t4-файл, который написал я. Просто добавьте его в проект, и измените пути к файлам, которые необходимо обрабатывать.
Заключение
Принцип DRY (Do not repeat yourself) – это один из основополагающих принципов в программировании. Его соблюдение дает множество преимуществ. И даже если без повторения одного и того же кода не обойтись, как это имеет место быть в случае Ajax-приложений с общей моделью данных, T4-шаблоны могут помочь в этой ситуации, и сгенерировать нужный код автоматически.К слову, генерация JS-кода на основе C# также является паттерном, одним из т.н. Ajax-паттернов, т.е. специально заточенных под Ajax-приложения. Этот паттерн называется Server-Side Code Generation.
Другим способом генерации JS на основе C#, более общим, является использование Script# – этот проект позволяет компилировать С#-код в JavaScript, и активно используется даже во внутренней разработке в компании Microsoft. В частности, судя по всему, SharePoint Client Object Model тоже написан на Script#.
спасибо за статью, полезно
ОтветитьУдалитьбуду применять, сам страдаю от того, что приходится дважды определять функциональность как на стороне сервера, так и на клиенте
что касается asp.net ajax - мне лично нравится та модель, которая там реализована и применительно к объявлениям классов, неймспейсов, и валидации параметров, объявления делегатов и прочее...и не понимаю подхода, когда в приложениях asp.net эта библиотека полностью игнорируется (начиная от sys.application.add_load handler) и вместо нее используется лес из всякого рода надстроек и плагинов jQuery, но это тема отдельной дискуссии:)
согласен! кстати, мне даже ASP.Net AJAX Templates нравятся немного больше, чем KnockOutJs - хотя тоже не идеальны.
Удалитьи я за собой заметил, что вообще стараюсь избегать jQuery, тем более что она по умолчанию в SharePoint'e не подключена, в отличие опять же от ASP.Net Ajax... ведь если её вдруг несколько разных солюшенов захотят подключить на одну страницу, будут проблемы.
в конце концов, есть ведь еще HTML5, который уже сейчас дублирует большую часть фундаментального функционала jQuery...