Создание шаблонов T4 во время выполнения (время сборки)? - PullRequest
21 голосов
/ 22 февраля 2010

Мы создаем собственное приложение, которое должно генерировать HTML-файлы для загрузки в списки eBay. Мы собираемся использовать шаблонизатор для генерации HTML-файлов на основе базы данных и статических полей, которые мы предварительно определили. Шаблон также должен иметь логические возможности (if-then, foreach и т. Д.).

Мы посмотрели на T4, и он выглядит отлично, но мы не видим ничего о том, есть ли у него возможности, которые будут использоваться во время выполнения, чтобы пользователь мог создать шаблон T4, а затем приложение может «скомпилировать» это и генерирует окончательный файл HTML. Возможно ли это и как?

Если нет, есть ли другие платформы, на которые мы должны обратить внимание, обладающие всеми этими возможностями?

Ответы [ 7 ]

16 голосов
/ 22 февраля 2010

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

По сути, он работает как старый стиль ASP, вы окружаете код C # блоками <%...%> и можете выдавать результаты, используя <%= expression %>.

Вы можете передать один объект в код шаблона, который, конечно, может быть любым типом объекта или просто массивом параметров. Вы также можете ссылаться на свои собственные сборки, если хотите выполнить пользовательский код.

Вот как будет выглядеть класс:

<%
var parameters = (string[])data;
var namespaceName = parameters[0];
var className = parameters[1];
%>
namespace <%= namespaceName %>
{
    public class <%= className %>
    {
    }
}

Конечно, вы можете перебирать вещи:

<% foreach (var parameter in parameters) { %>
<%= parameter %>
<% } %>

и поместить код в if-блоки и т. Д.

Библиотека классов выпущена на CodePlex здесь:

а также на NuGet .

Проект поставляется с примерами, скачайте исходный код или просмотрите его онлайн .

Чтобы ответить на вопросы по электронной почте также здесь, чтобы другие могли видеть:

  1. Все типы кода C # , которые вписываются в вызов метода , могут быть скомпилированы в шаблоне. Он запускает нормальный код C # 3.5 со всем, что означает, что нет никаких искусственных ограничений. Единственное, что нужно знать, так это то, что любой, если, хотя, for, foreach и т. Д. Код, содержащий код шаблона для отправки, должен использовать фигурные скобки, вы не можете сделать однострочный блок типа if-then. Ниже приведено описание ограничения вызова метода.
  2. Параметр data соответствует тому, что было передано в качестве параметра методу .Generate(x) из вашего приложения, и относится к тому же типу. Если вы передаете объект, который вы определили в своих собственных библиотеках классов, вам нужно добавить ссылку на код шаблона для правильного доступа к нему. (<%@ reference your.class.library.dll %>)
  3. Если вы повторно используете скомпилированный шаблон, по сути это будет только вызов метода для класса, никаких дополнительных накладных расходов на фактический вызов .Generate() не производится. Если вы не позвоните .Compile() самостоятельно, об этом позаботится первый звонок .Generate(). Также обратите внимание, что код выполняется в отдельном домене приложения, поэтому есть небольшая накладная нагрузка, связанная с копированием параметра и результатом туда и обратно. Однако код выполняется с нормальной скоростью кода JITted .NET.

Пример блока if:

<% if (a == b) { %>
This will only be output if a==b.
<% } %>

Также нет никаких искусственных ограничений на форматирование кода, выберите стиль, который вам больше подходит:

<%
    if (a == b)
    {
%>
This will only be output if a==b.
<%
    }
%>

Только обратите внимание, что все некодовые части шаблона будут выводиться как есть, что означает вкладки и такие последующие %> блоки, которые также будут выводиться.

Существует одно ограничение: весь код, который вы пишете, должен помещаться внутри одного вызова метода.

Позвольте мне объяснить.

Механизм работы шаблонов заключается в том, что он создает файл .cs и передает его компилятору C #, этот файл .cs примерно выглядит так:

using directives

namespace SomeNamespace
{
    public class SomeClass
    {
        public string Render(object data)
        {
            ... all your code goes here
        }
    }
}

Это означает, что вы не можете определять новые классы, новые методы, поля уровня класса и т. Д.

Однако вы можете использовать анонимных делегатов для внутреннего создания функций. Например, если вы хотите единообразный способ форматирования дат:

Func<DateTime, string> date2str = delegate(DateTime dt)
{
    return dt.ToString("G");
};

тогда вы можете просто использовать это в оставшейся части кода шаблона:

<%= date2str(DateTime.Now) %>

Единственное требование, которое у меня есть, это то, что вы не загружаете файлы в Интернет и не утверждаете, что написали код, кроме того, что вы можете делать с ним все, что хотите.

Редактировать 23.04.2011: Исправлены ссылки на проект CodePlex.

14 голосов
/ 18 мая 2010

Если вы можете использовать Visual Studio 2010 для создания и редактирования шаблонов, то вы можете использовать предварительно скомпилированные шаблоны, которые были разработаны именно для этого сценария и являются поддерживаемым вариантом от Microsoft.

Вы разрабатываете шаблон в Visual Studio, предварительно компилируете его и внедряете сборку, которая не зависит от Visual Studio, вместе с вашим приложением.

http://www.olegsych.com/2009/09/t4-preprocessed-text-templates/

6 голосов
/ 22 февраля 2010

Сборка, которая реализует преобразование текста T4, - Microsoft.VisualStudio.TextTemplating.dll, которая поставляется вместе с Visual Studio.

Если вы хотите начать с первых принципов, вам нужно реализовать Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost и передать свою реализацию в качестве аргумента Microsoft.VisualStudio.TextTemplating.Engine .ProcessTemplate () , который будет выполнять преобразование.

Это дает вам больше гибкости, чем обращение к TextTransform.exe.

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

Распространение этой сборки позволит избежать необходимости установки Visual Studio.

3 голосов
/ 02 марта 2010

Полностью возможно использовать T4 во время выполнения.

Microsoft на самом деле не поддерживает этот сценарий в .NET 3.5. Похоже, что .NET 4.0 получит лучшую поддержку от Microsoft.

Mono обеспечивает некоторую поддержку этого сценария в .NET 3.5.

Я успешно проверил эту концепцию на .NET 3.5 с помощью реализации Mono T4, но готовое решение этой проблемы для .NET 3.5 потребовало бы гораздо больше усилий, чем я вложил до сих пор.

Вы можете найти реализацию Mono T4 здесь:

https://github.com/mono/monodevelop/tree/master/main/src/addins/TextTemplating

Я задокументировал некоторые проблемы, с которыми я столкнулся при попытке запуска шаблонов T4 из кода .NET, здесь:

Параметры запуска шаблонов T4 из кода .NET

3 голосов
/ 22 февраля 2010

Шаблоны T4 можно скомпилировать с помощью инструмента командной строки TextTransform.exe .Ваше приложение может создать файл .tt, а затем вызвать TextTransform.exe для создания выходных данных.

0 голосов
/ 13 сентября 2018

Жидкость может быть хорошим выбором для этого. Это язык шаблонов с открытым исходным кодом, подробнее о языке здесь: https://shopify.github.io/liquid/

Вот реализация для .NET: https://github.com/dotliquid/dotliquid

Синтаксис довольно приятный. Вот пример кода для C #:

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public List<string> Friends { get; set; }
    }

    static void Main(string[] args)
    {
        Template.RegisterSafeType(typeof(Person), new string[]
            {
                nameof(Person.Name),
                nameof(Person.Age),
                nameof(Person.Friends),
            });

        Template template = Template.Parse(
@"<h1>hi {{name}}</h1> 
<p>You are{% if age > 42' %} old {% else %} young{% endif %}.</p>
<p>You have {{ friends.size }} friends:</p>
{% assign sortedfriends = friends | sort %}
{% for item in sortedfriends -%}
  {{ item | escape }} <br />
{% endfor %}

");
        string output = template.Render(
            Hash.FromAnonymousObject(
                new Person()
                {
                    Name = "James Bond",
                    Age = 42,
                    Friends = new List<string>()
                    {
                        "Charlie",
                        "<TagMan>",
                        "Bill"
                    }
                } ));

        Console.WriteLine(output);

/* The output will be: 

<h1>hi James Bond</h1>
<p>You are young.</p>
<p>You have 3 friends:</p>

  &lt;TagMan&gt; <br />
  Bill <br />
  Charlie <br />             

*/

    }
0 голосов
/ 27 ноября 2012

Одна ошибка, которую я сделал, я добавил файл "Текстовый шаблон". Чтобы создать текст в время выполнения , выберите «Шаблон предварительно обработанного текста». Если вы изначально выбрали «Текстовый шаблон», то легко изменить пользовательский инструмент на «TextTemplatingFilePreprocessor» в свойствах файла в VS.

...