在运行时(构建时)创建 T4 模板?

Posted

技术标签:

【中文标题】在运行时(构建时)创建 T4 模板?【英文标题】:Creating T4 templates at runtime (build-time)? 【发布时间】:2011-01-19 10:30:00 【问题描述】:

我们正在构建一个内部应用程序,它需要生成 html 文件以上传到 eBay 列表。我们希望使用模板引擎根据我们预定义的数据库和静态字段生成 HTML 文件。模板还需要具备逻辑能力(if-then、foreach 等)。

我们看过 T4,它看起来很完美,但我们没有看到任何关于它是否具有在运行时使用的功能,以便用户可以创建 T4 模板,然后应用程序可以“编译”它并生成最终的 HTML 文件。这可能吗?如何实现?

如果没有,是否还有其他具有所有这些功能的框架?

【问题讨论】:

【参考方案1】:

T4 模板可以使用TextTransform.exe 命令行工具进行编译。你 可以让您的应用程序创建一个 .tt 文件,然后调用 TextTransform.exe 来生成输出。

【讨论】:

这个解决方案不需要在用户的机器上安装Visual Studio吗?这对我们来说是一个限制。 Mono 具有相当强大的文本转换实现。如果依赖关系是一个问题,我会看看。 是的,目前,它依赖于 Visual Studio。这将随着 VS2010 / .NET 4 和所谓的预编译 T4 模板而改变。请参阅此处了解信息:olegsych.com/2009/09/t4-preprocessed-text-templates【参考方案2】:

我有一组类似的类用于此,将模板化文本生成嵌入到软件中。

基本上,它的工作方式类似于旧式 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 上发布:

CodePlex: TextTemplate

以及NuGet。

项目自带示例,下载源码或browse it online.

也可以在这里通过电子邮件回答问题,供其他人查看:

    适合方法调用的所有类型的 C# 代码都可以在模板中编译。它运行正常的 C# 3.5 代码,这意味着没有人为限制。唯一要知道的是,任何包含要发出的模板代码的 if、while、for、foreach 等代码都必须使用大括号,您不能执行单行 if-then 类型块。有关方法调用限制,请参见下文。 data 参数对应于作为参数从您的应用程序传递给.Generate(x) 方法的任何内容,并且属于同一类型。如果您传入您在自己的类库中定义的对象,则需要添加对模板代码的引用才能正确访问它。 (&lt;%@ reference your.class.library.dll %&gt;) 如果你重用编译后的模板,它本质上只是一个类的方法调用,对.Generate()的实际调用没有额外的开销。如果您不自己拨打.Compile(),第一次拨打.Generate() 将负责处理。另请注意,代码在单独的应用程序域中运行,因此与来回复制参数和结果相关的编组开销很小。但是,代码以正常的 JITted .NET 代码速度运行。

if-block 示例:

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

对代码格式也没有人为限制,选择最适合你的样式:

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

请注意,模板的所有非代码部分几乎都将按原样输出,这意味着标签和类似%&gt; 块也将被输出。

有一个限制,您编写的所有代码都必须适合单个方法调用。

让我解释一下。

模板引擎的工作方式是生成一个 .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 项目的链接。

【讨论】:

Wooow O.o 我正在下载这个,我会告诉你进展如何。如果它做到了你所说的一切,你将成为一个绝对的救星! 现在编辑了答案,正如您在电子邮件中所说,“数据”变量不存在,因为它被命名为“数据”。它是一个方法参数,因此是小写的“d”。 “模板引擎的工作方式是生成一个 .cs 文件并将其提供给 C# 编译器”:这是否也意味着运行它的机器需要 VisStudio 或至少其他东西安装?或者有没有一种方法可以让您以编程方式调用 C# 编译器?我不认为编译器是随 .net 运行时一起提供的......虽然由于 IIS 可以即时编译 ASP.NET,但我想它一定在某个地方...... @Lasse 哇,当他们添加该功能时,我想我错过了。谢谢! 要访问命令行外部编译器,您需要安装 SDK,但编译器本身是 .NET 运行时的一部分,除非您安装“客户端配置文件运行时”,我认为缺少编译器。另外,准确地说,我实际上并没有生成 file 本身,我在内存中生成源代码并将其提供给 C# 编译器类,在我询问之前添加对程序集的引用和设置选项它来编译代码。磁盘上没有任何文件,即使程序集是在内存中生成的。【参考方案3】:

实现 T4 文本转换的程序集是 Microsoft.VisualStudio.TextTemplating.dll,它随 Visual Studio 一起提供。

如果你想从第一原则开始,你需要实现Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost,并将你的实现作为参数传递给Microsoft.VisualStudio.TextTemplating.Engine.ProcessTemplate(),Microsoft.VisualStudio.TextTemplating.Engine.ProcessTemplate()将执行转换。

与调用 TextTransform.exe 相比,这为您提供了更大的灵活性。

但是,如果您的代码是发货产品,则不清楚该程序集的许可是什么,以及您是否有权将其与您的应用程序一起重新分发。

重新分发此程序集将避免安装 Visual Studio。

【讨论】:

不幸的是,由于许可问题,这不能满足我们的需求,因为该产品将被运送给最终用户。无论如何谢谢:) Mono 项目也有一个开源实现。 anonsvn.mono-project.com/viewvc/trunk/monodevelop/main/src/…【参考方案4】:

完全可以在运行时使用 T4。

Microsoft 在 .NET 3.5 中并没有以任何合理的方式真正支持这种情况。听起来 .NET 4.0 会得到微软更好的支持。

Mono 在 .NET 3.5 中确实为这种情况提供了一些支持。

在 Mono T4 实现的帮助下,我已经在 .NET 3.5 上成功地证明了这个概念,但是针对 .NET 3.5 的这个问题的现成解决方案需要比我迄今为止所投入的更多努力。

您可以在此处找到 Mono T4 实现:

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

我在这里记录了尝试从 .NET 代码运行 T4 模板时遇到的一些问题:

Options for running T4 templates from .NET code

【讨论】:

【参考方案5】:

如果您可以使用 Visual Studio 2010 来创建和编辑模板,那么您可以使用预编译模板,这些模板正是为这种情况而设计的,并且是 Microsoft 支持的选项。

您在 Visual Studio 中设计模板,对其进行预编译并部署一个不依赖于 Visual Studio 的程序集以及您的应用程序。

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

【讨论】:

链接已损坏。【参考方案6】:

我犯的一个错误是我添加了一个“文本模板”文件。要在运行时生成文本,请选择“预处理文本模板”。如果您最初选择“文本模板”,则在 VS 的文件属性中将自定义工具设置为“TextTemplatingFilePreprocessor”是一个简单的更改。

【讨论】:

【参考方案7】:

液体可能是一个不错的选择。这是一种开源模板语言,请在此处阅读有关该语言的更多信息: 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 />             

*/

    

【讨论】:

以上是关于在运行时(构建时)创建 T4 模板?的主要内容,如果未能解决你的问题,请参考以下文章

如何在构建时在 Blazor 项目中运行 T4 模板

T4模板:T4模板之基础篇

T4模板:将现有文件转换为运行时模板

使用T4模板动态生成邮件内容并储存到任意位置

T4模板之基础篇

T4模板之基础篇