Создание шаблонов T4 во время выполнения (время сборки)?

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

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

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


person Amberite    schedule 21.02.2010    source источник


Ответы (7)


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

По сути, он работает как старый 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 со всем, что означает отсутствие искусственных ограничений. Единственное, что нужно знать, это то, что любой код if, while, for, foreach и т. д., который содержит код шаблона для генерации, должен использовать фигурные скобки, вы не можете сделать однострочный блок типа if-then. См. ниже ограничения вызовов методов.
  2. Параметр data соответствует тому, что было передано в качестве параметра методу .Generate(x) из вашего приложения, и имеет тот же тип. Если вы передаете объект, который вы определили в своих собственных библиотеках классов, вам необходимо добавить ссылку на код шаблона, чтобы правильно получить к нему доступ. (<%@ reference your.class.library.dll %>)
  3. Если вы повторно используете скомпилированный шаблон, по сути, это будет только вызов метода класса, никаких дополнительных накладных расходов на фактический вызов .Generate() не производится. Если вы не позвоните .Compile() сами, об этом позаботится первый вызов .Generate(). Также обратите внимание, что код выполняется в отдельном домене приложения, поэтому есть небольшие накладные расходы, связанные с копированием параметра и результата туда и обратно. Однако код выполняется с нормальной скоростью JIT-кода .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.

person Lasse V. Karlsen    schedule 21.02.2010
comment
Wooow O.o Я загружаю это прямо сейчас, и я дам вам знать, как это происходит. Если он делает все, о чем вы говорите, вы будете абсолютным спасателем жизни! - person Amberite; 22.02.2010
comment
Отредактировал ответ сейчас, как вы сказали в электронном письме, переменная данных не существует, потому что она называется данными. Это параметр метода, поэтому d в нижнем регистре. - person Lasse V. Karlsen; 22.02.2010
comment
Механизм шаблонов работает следующим образом: он создает файл .cs и передает его компилятору C#: не означает ли это также, что на машине, на которой он работает, должен быть установлен VisStudio или, по крайней мере, что-то еще? Или есть способ программно вызвать компилятор С#? Я не думал, что компилятор поставляется с исполняющей средой .net... Хотя, поскольку IIS может компилировать ASP.NET на лету, я думаю, что он должен быть где-то там... - person CodingWithSpike; 23.02.2010
comment
@Lasse Вау, думаю, я пропустил это, когда они добавили эту функцию. Спасибо! - person CodingWithSpike; 23.02.2010
comment
Чтобы получить доступ к внешним компиляторам командной строки, вам необходимо установить SDK, но сам компилятор является частью среды выполнения .NET, если только вы не установите среду выполнения профиля клиента, в которой, как мне кажется, отсутствует компилятор. Кроме того, если быть точным, я на самом деле не создаю файл как таковой, я создаю исходный код в памяти и передаю его классу компилятора C#, добавляя ссылки на сборки и устанавливая параметры, прежде чем я спрошу это для компиляции кода. Ни в одном из них нет файла на диске, даже сборка создается в памяти. - person Lasse V. Karlsen; 24.02.2010
comment
Отличный код, работает без нареканий. Плюс отличная поддержка от Лассе. Слава! - person Amberite; 24.02.2010
comment
@Lasse: Ссылки больше не работают. Как я могу получить этот код? - person epitka; 08.04.2010
comment
Размещены обновленные ссылки. К сожалению, на данный момент нет репозитория бинарных файлов, я посмотрю, как опубликовать новые для этого, но, поскольку бинарный файл был просто сборкой ствола, я решил, что если я хочу вернуть поддержку бинарных файлов, он должен быть в более контролируемым образом. - person Lasse V. Karlsen; 08.04.2010
comment
Дайте мне знать, если у вас есть какие-либо вопросы, либо в комментариях здесь, либо по электронной почте на адрес [email protected] - person Lasse V. Karlsen; 08.04.2010
comment
Опубликовал ссылку на шаблон, который я использую в проекте, чуть ниже ссылок на репозиторий, надеюсь, это даст вам несколько примеров того, как что-то делать с этим кодом. - person Lasse V. Karlsen; 08.04.2010
comment
Похоже, все ссылки снова не работают? У вас еще есть где-нибудь эти образцы? - person murki; 14.04.2011

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

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

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

person GarethJ    schedule 18.05.2010
comment
Ссылка не работает. - person Dan; 23.07.2019

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

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

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

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

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

person Leon Breedt    schedule 22.02.2010
comment
К сожалению, это не соответствует нашим потребностям из-за проблем с лицензией, так как этот продукт БУДЕТ поставляться конечным пользователям. В любом случае спасибо :) - person Amberite; 24.02.2010
comment
Существует также реализация проекта Mono с открытым исходным кодом. anonsvn.mono-project.com/viewvc/trunk /monodevelop/main/src/ - person Filip Frącz; 15.06.2010

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

person CodingWithSpike    schedule 21.02.2010
comment
Разве Visual Studio не нужно устанавливать на компьютер пользователя с этим решением? Это ограничение для нас. - person Amberite; 22.02.2010
comment
Mono имеет довольно мощную реализацию преобразования текста. Я бы посмотрел на это, если проблемы с зависимостями. - person Sky Sanders; 22.02.2010
comment
Да, на данный момент это зависит от Visual Studio. Это изменится с VS2010/.NET 4 и так называемыми предварительно скомпилированными шаблонами T4. Информацию см. здесь: olegsych.com/2009/09/t4- предварительно обработанные текстовые шаблоны - person marc_s; 22.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< /а>

person Michael Maddox    schedule 02.03.2010

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

person Clay Lenhart    schedule 27.11.2012

Жидкость может быть хорошим выбором для этого. Это язык шаблонов с открытым исходным кодом, подробнее об этом языке читайте здесь: 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 />             

*/

    }
person PEK    schedule 13.09.2018