在运行时(构建时)创建 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)
方法的任何内容,并且属于同一类型。如果您传入您在自己的类库中定义的对象,则需要添加对模板代码的引用才能正确访问它。 (<%@ reference your.class.library.dll %>
)
如果你重用编译后的模板,它本质上只是一个类的方法调用,对.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.
<%
%>
请注意,模板的所有非代码部分几乎都将按原样输出,这意味着标签和类似%>
块也将被输出。
有一个限制,您编写的所有代码都必须适合单个方法调用。
让我解释一下。
模板引擎的工作方式是生成一个 .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>
<TagMan> <br />
Bill <br />
Charlie <br />
*/
【讨论】:
以上是关于在运行时(构建时)创建 T4 模板?的主要内容,如果未能解决你的问题,请参考以下文章