Использование Razor отдельно от ASP.NET MVC

от автора

По роду деятельности студентом мне часто приходится писать различные лабораторные и курсовые работы. Неотъемлемая часть этих работ(как и в 80% задач бизнес-программирования) — код составления отчета о найденных элементах в базе данных или выполненной длительной и сложной операции. В десктопном программировании в рамках .NET Framework есть различные способы решения подобных проблем:

  • Старая школа: формат RTF никто не отменял, однако вручную составить что-либо сложнее цветного текста со шрифтами разного цвета и ссылками лично у меня не получалось. Способ действенный, но стандарт не самый легкий для понимания, на последнюю версию даже смотреть страшно
  • Для Silverlight/WPF — формирование FlowDocument своими руками. Объектная модель однозначно удобнее формирования старого формата вручную, но вариант тоже только для смелых
  • Сторонние решения вроде Crystal Reports, возможно и очень мощный инструмент для создания отчетов, но не всегда в этом есть необходимость, к тому же не всегда есть возможность тянуть пару-тройку дополнительных программных продуктов1
  • Использование Microsoft Office Interop. Нет, это издевательство. Ручная работа с памятью в управляемой среде вызывает лишь бурю радости. Начинающий программист выпьет не одну упаковку кофе, прежде чем смирится с подобным принципом работы.
  • Формирование HTML. На нашу радость, стандарт позволяет создать документы средней степени форматированности, с помощью CSS можно довести до ума. Минусы — оформить документ по всем требованиям крайне затруднительно. Но это если в этом есть необходимость. Плюсы: простота стандарта и всеобщий переход «в облака и браузеры» стимулирует развитие HTML.

О последнем я и буду вести речь.

Самый простой и очевидный вариант — формирование HTML строки вручную. Те, кто только знакомится с платформой, тут же начнут заниматься конкатенацией объектов String, кто читал документацию — обратят внимание на класс StringBuilder. В любом случае, String.Format не всегда самый удобный способ формирования отчетов. Зато коллеги по платформе, пользующиеся ASP.NET MVC никак не могут нарадоваться движку представлений Razor. И тут возникает вопрос: а почему бы и не использовать мощь ASP.NET Razor в своих целях?
Прежде всего, необходимо подключить сборку System.Web.Razor. Она из поставки ASP.NET MVC, так что если у целевого компьютера он не установлен, следует позаботиться о локальном копировании необходимых dll’ок.
Объявим базовый класс, от которого будет наследоваться наше представление

    public abstract class TemplateBase     {         public StringBuilder Buffer { get; set; }         public StringWriter Writer { get; set; }          public TemplateBase()         {             Buffer = new StringBuilder();             Writer = new StringWriter(Buffer);         }          public abstract void Execute();          // Записывает в строку выражения типа: "@foo.Bar"         public virtual void Write(object value)         {             // Don't need to do anything special             // Razor for ASP.Net does HTML encoding here.             WriteLiteral(value);         }          // Записывает литералы разметки: "<p>Foo</p>"         public virtual void WriteLiteral(object value)         {             Buffer.Append(value);         }          // ... а здесь мы можем указать еще какие-либо публичные свойства, которые будут передаваться движку Razor вне зависимости от того, хочет он этого или нет     } 

Прежде всего, необходимо инициализировать движок.

private RazorTemplateEngine SetupRazorEngine()         {             // 1. Указываем, что хотим использовать C# описания представления             RazorEngineHost host = new RazorEngineHost(new CSharpRazorCodeLanguage());              // 2. Указываем базовый класс, от которого наследуется представление. Тот самый, что описывали выше             host.DefaultBaseClass = typeof(TemplateBase).FullName;              // 3. Указываем пространство имен и название класса представления             host.DefaultNamespace = "RazorOutput";             host.DefaultClassName = "Template";              // 4. Указываем используемые по умолчанию пространства имен (using)             host.NamespaceImports.Add("System");              // 5. Создаем новый движок представления             return new RazorTemplateEngine(host);         } 

Как известно, .NET поддерживает компиляцию кода «на лету». Эту особенность и использует Razor. Следовательно в нашем случае надо прочитать файл с шаблоном и сохранить его в отдельную сборку.

            // Генерируем код шаблона             GeneratorResults razorResult = null;             using (TextReader rdr = new StringReader( MyTemplatesString  ))                 razorResult = _engine.GenerateCode(rdr);              CSharpCodeProvider codeProvider = new CSharpCodeProvider();                         // Записываем компилированный код в файл сборки             string outputAssemblyName = String.Format("Temp_{0}.dll", Guid.NewGuid().ToString("N"));             CompilerResults results = codeProvider.CompileAssemblyFromDom(                 new CompilerParameters(new string[] {                     typeof(Form1).Assembly.CodeBase.Replace("file:///", "").Replace("/", "\\")                 }, outputAssemblyName),                 razorResult.GeneratedCode); 

Если все прошло хорошо, то дальше использование простое:

                Assembly asm = Assembly.LoadFrom(outputAssemblyName);                 if (asm == null)                 {                     MessageBox.Show("Ошибка во время загрузки сборки");                 }                 else                 {                     Type typ = asm.GetType("RazorOutput.Template");                     if (typ == null)                     {                         MessageBox.Show("Не найден RazorOutput.Template в сборке {0}", asm.FullName);                     }                     else                     {                         TemplateBase newTemplate = Activator.CreateInstance(typ) as TemplateBase;                         if (newTemplate == null)                         {                             MessageBox.Show("Невозможно создать экземпляр RazorOutput.Template или он не наследуется TemplateBase");                         }                         else                         {                             // здесь мы присваиваем newTemplate все необходимые нам данные                             // ...                             newTemplate.Execute();                             string resultHTML = newTemplate.Buffer.ToString();                             newTemplate.Buffer.Clear();                             return resultHTML; // разумеется никто в здравом уме не будет здесь возвращать String, но это исключительно для примера                         }                     }              } 

Вот, собственно и все. Остается только вопрос, как именно указывать данные представления — через TemplateBase, или же через Razor-синтаксис вида

@functions { 	public string CustomerName { get; set; } 	public string ResetLink { get; set; } } 

В последнем случае вам придется отказаться от динамической генерации шаблонов, зато очень легко получить доступ к этим данным.

Итак, с помощью нехитрых движений мы избежали искушения написания «еще одного» шаблонизатора, воспользовавшись мощным и удобным средством от MS


1 предполагается, что раз вы пишете под .NET Framework, то он предполагается установленным у клиента. А вот Office, Crystal Reports, SQL Server, Visual Studio — не обязательно

ссылка на оригинал статьи http://habrahabr.ru/post/173923/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *