Pull to refresh

Использование провайдеров компиляции в Asp.net

Reading time 6 min
Views 2.1K

В качестве вступления



В Asp.net любая интернет-страница представляется в виде двух файлов: *.aspx и *.aspx.cs. В *.aspx-файлах содержится html-подобная разметка самой страницы, а в *.aspx.cs-файлах код на языке C#, который представлен в виде отдельного класса.

В разметку страницы можно добавлять серверные элементы управления, например тегом <asp:Button ID=«MyButton» runat=«server» />.

Причём с каждым таким объявлением будет связана переменная, то есть в нашем случае мы получим доступ к переменной типа Button и именем MyButton, хотя на первый взгляд эта переменная нигде не объявлена. (Хотя в первой версии Asp.net объявление переменных вставлялись в тот же файл.)

На самом деле это не так. Класс, описаный в *.aspx.cs-файле является частичным (он помечен модификатором partial), одна его часть описана в *.aspx.cs-файле, а вторая находится во временном файле, который генерируется на основании просмотра *.aspx-файла. Генерацией этого временного файла как раз и занимается провайдер компиляции.

Пишем провайдер компиляции



1. Определимся, что будет делать провайдер компиляции.

Чтобы особо не мудрить, пусть он выполняет преобразование xml-файла вида:

  1. <classes>
  2.    <class name=«С1»>
  3.       <property type=«int» name=«x1» value=«17» />
  4.       <property type=«string» name=«x2» value=«Hello, World!» />
  5.    </class>
  6.    <class name=«С2»>
  7.       <property type=«int» name=«x1» value="-5" />
  8.       <property type=«string» name=«x2» value=«Это строка!» />
  9.    </class>
  10. </classes>
* This source code was highlighted with Source Code Highlighter.


в статический класс, содержащий константы.

2. Создадим новый проект. В качестве типа проекта выберем «Class Library», назовём его «MyLib».

3. Все провайдеры компиляции являются производными от класса «System.Web.Compilation.BuildProvider», поэтом добавим ссылку на сборку «System.Web» и используемые пространства имён.

Конструктор класса ничего не выполняет, а вот метод «GenerateCode» нам потребуется переопределить. В итоге должно получиться следующее:

  1. using System;
  2. using System.Web.Compilation;
  3. using System.Xml;
  4. using System.IO;
  5. using System.Web.Hosting;
  6.  
  7. namespace MyLib
  8. {
  9.   public class MyBP: BuildProvider
  10.   {
  11.     public MyBP() { }
  12.  
  13.     public override void GenerateCode(AssemblyBuilder assemblyBuilder)
  14.     {
  15.       //В переменную writer будем записывать генерируемый код
  16.       using (TextWriter writer = assemblyBuilder.CreateCodeFile(this))
  17.       {
  18.         //Создаём xml-документ используя путь к анализируемому файлу
  19.         XmlDocument xml = new XmlDocument();
  20.         xml.Load(VirtualPathProvider.OpenFile(base.VirtualPath));
  21.  
  22.         //Получаем все классы
  23.         XmlNodeList classes = xml.GetElementsByTagName(«class»);
  24.         foreach (XmlNode _class in classes)
  25.         {
  26.           //Получаем название класса
  27.           string cName = _class.Attributes[«name»].Value;
  28.           if (cName == null || cName.Length == 0) continue;
  29.  
  30.           //Получаем поля класса
  31.           string cProperties = "";
  32.  
  33.           //Получаем все поля
  34.           XmlNodeList properties = _class.ChildNodes;
  35.           foreach (XmlNode property in properties)
  36.           {
  37.             if (property.Name != «property») continue;
  38.  
  39.             //Получаем тип поля
  40.             string pType = property.Attributes[«type»].Value;
  41.             if (pType == null || pType.Length == 0) continue;
  42.  
  43.             //Получаем название поля
  44.             string pName = property.Attributes[«name»].Value;
  45.             if (pName == null || pName.Length == 0) continue;
  46.  
  47.             //Получаем значение поля
  48.             string pValue = property.Attributes[«value»].Value;
  49.             if (pValue == null || pValue.Length == 0) continue;
  50.  
  51.             //Если поле имеет строковой тип, берём его в кавычки
  52.             if (pType == «string») pValue = string.Format("\"{0}\"", pValue);
  53.  
  54.             //Добавляем поле
  55.             cProperties += string.Format(" public const {0} {1} = {2}; ", pType, pName, pValue);
  56.           }
  57.  
  58.           //Добавляем класс
  59.           writer.Write(" public static partial class " + cName + " { " + cProperties + " } ");
  60.         }
  61.       }
  62.     }
  63.   }
  64. }
* This source code was highlighted with Source Code Highlighter.


Компилируем и получаем сборку.

4. Создаём новый сайт (Назовём его «MySite»). Первое, что нужно сделать, создать папки bin и App_Code. В папку bin скопируем только что созданную библиотеку. Теперь нужно подключить провайдер компиляции. Для этого идём в файл Web.config и в разделе «compilation» добавляем блок:

  1. <buildProviders>
  2.    <add type=«MyLib.MyBP, MyLib» extension=".cc" />
  3. </buildProviders>
* This source code was highlighted with Source Code Highlighter.


Это определение говорит, что для файлов с расширением «cc» будет использован провайдер компиляции MyLib.MyBP, который будет взят из сборки MyLib".

5. Создаём файл «my.cc» в папке «App_Code» с xml-кодом, вроде того, что приведён выше. Через некоторое время (обычно секунд десять, когда файл обработается) в любом C#-коде можно будет использовать классы «C1» и «C2» и их константные поля. (Результаты можно увидеть на скриншотах)

6. Скриншоты:

Документ, обрабатываемый провайдером компиляции:


Появились классы «C1» и «C2»:


И их поля:


Ну и в качестве концовки



Безусловно данный пример не может претендовать на полноценный провайдер компиляции, а показывает только общую идею, в коде я опустил многие проверки, обработку исключений пожертвовал оптимальностью ради сокращения кода.

PS: Я не нашёл способа не помещать провайдер в отдельную сборку, а просто добавить класс в папку App_Code. В данном случае возникает ошибка о невозможности загрузить тип.

Надеюсь, что найдутся люди, которые знают решение данной проблеммы.
Литература: Дино Эспозито «Asp.net 2.0 Углублённое изучение».

Огромное спасибо выражаю пользователю zabr за советы и помощь, а так же всем, кто помог опубликовать этот топик.
Tags:
Hubs:
+42
Comments 39
Comments Comments 39

Articles