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

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

我们已经看过T4并且它看起来很完美,但我们没有看到它是否具有在运行时使用的function,因此用户可以创建T4模板,然后应用程序可以“编译”它并生成最终的HTML文件。 这有可能,怎么样?

如果没有,是否还有其他我们应该关注的具有所有这些function的框架?

我使用了类似的一组类,将模板文本生成嵌入到软件中。

基本上,它就像旧式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-blocks等中。

类库在CodePlex上发布:

  • CodePlex:TextTemplate

以及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()就会处理它。 另请注意,代码在单独的appdomain中运行,因此有一个轻微的编组开销,与来回复制参数和结果有关。 但是,代码以正常的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. <% } %> 

只需注意模板的所有非代码部分几乎都按原样输出,这意味着还将输出制表符和后面的%>块。

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

让我解释。

模板引擎的工作方式是它生成.cs文件并将其提供给C#编译器,这个.cs文件粗略地看起来像这样:

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

这意味着您无法定义新类,新方法,类级字段等。

但是,您可以使用匿名委托在内部创建函数。 例如,如果您想要一种统一格式化日期的方法:

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

然后你可以在其余的模板代码中使用它:

 <%= date2str(DateTime.Now) %> 

只有我的要求是你没有将文件上传到网上并声称你编写了代码,除了你可以自由地用它做你想做的事情。

编辑23.04.2011:修复了CodePlex项目的链接。

如果您可以使用Visual Studio 2010进行模板创建和编辑,则可以使用预编译模板,这些模板专为此方案而设计,并且是Microsoft支持的选项。

您在Visual Studio中设计模板,预编译它并部署一个与您的应用程序一起不依赖Visual Studio的程序集。

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

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

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

这比调用TextTransform.exe更灵活。

但是,如果您的代码是发货产品,则不清楚此程序集的许可是什么,以及您是否有权使用您的应用程序重新分发它。

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

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

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

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

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

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

你可以在这里找到Mono T4实现:

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

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

从.NET代码运行T4模板的选项

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

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

hi {{name}}

You are{% if age > 42' %} old {% else %} young{% endif %}.

You have {{ friends.size }} friends:

{% assign sortedfriends = friends | sort %} {% for item in sortedfriends -%} {{ item | escape }}
{% endfor %} "); string output = template.Render( Hash.FromAnonymousObject( new Person() { Name = "James Bond", Age = 42, Friends = new List
() { "Charlie", "", "Bill" } } )); Console.WriteLine(output); /* The output will be:

hi James Bond

You are young.

You have 3 friends:

<TagMan>
Bill
Charlie
*/ }