示例代码:示例代码_for_真真理解T4_parameter指令.zip
本人最近在学习 T4 模板相关的知识,一些知料中文版的难找,所以翻翻老外的文章先译出来分享给大家。这里有篇入门文章 《你必须懂的T4模板:浅入深出》
Visual Studio 2010 引入了一个新的 T4 指令:参数指令。这个参数提供一种在文本模板中定义输入参数的标准方式。
语法
<#@ parameter name="…" type="…" #>
name 为参数指定名称,参数名称必须是 C# 或 VB 有效标示符。
type 为参数指定类型,参数类型必须包含完整的命名空间,eg:System.String 。也可以使用自定义类型,自定义类型的程序集必须使用 <#@ assembly #> 指令引入。
补充:CallContext 和LogicalCallContext
因为本文讲到远程调用的相关应用,译者了解不多,所以在此补充这两个概念。
1. CallContext 是类似于方法调用的线程本地存储的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽。数据槽不在其他逻辑线程上的调用上下文之间共享。
2. LogicalCallContext 类是在对远程应用程序域进行方法调用时使用的 CallContext 类的一个版本。当对另一个 AppDomain 中的对象进行远程方法调用时,CallContext 类将生成一个与该远程调用一起传播的 LogicalCallContext。
让CallContext实现跨线程传播
CallContext API如下图(注意:没有提供构造函数):
也就是说,如果想让CallContext的数据被自动传递到当目标线程,只能将其作为LogicalCallContext。我们有两种方法将相应的数据存储为LogicalCallContext:
1. 调用CallContext的静态方法LogicalSetData 。
2. 让 CallContext 传递的对象实现 ILogicalThreadAffinative 接口。实现 ILogicalThreadAffinative 接口并存储在 CallContext 中的对象会自动随 LogicalCallContext 传播到 AppDomain 外部。
欲知更多,请参考:如何实现对上下文(Context)数据的统一管理
使用
在模板转换期间,参数可做为只读的强类型属性在代码中使用(只生成属性的 get 访问器)。
MyTemplate.tt (设计时模板)
<#@template language="C#"#>
<#@Parameter Name="MyParameter" Type="System.String"#>
Parameter in statement block: <# Write(MyParameter == null ? "null" : MyParameter); #>
调用输出字符串:
Parameter in statement block: null
参数值既可以由远程的 CallContext提供,也可以由 Session 字典提供。典型的,模板参数会被应用在 T4 引擎实例或依赖于标准的 ITextTemplating 服务的 Visual Studio扩展中。虽然 Visual Studio 扩展是一个令人着迷的主题,但已不属于本文讨论的范围。而本文重点将说明使用模板代码本身的技术传递参数值。
CallContext
CallContext 允许你定义全局变量,变量只能在单个线程中访问,也称为“线程局部变量”。“线程局部变量”虽然表面上像存储在本地 Windows 的 CallContext ,但实际上是远程调用创建,无论是通过网络还是在 AppDomain 域之间传递 CallContext ,CallContext 将生成一个与该远程调用一起传播的 LogicalCallContext 。换句话说,CallContext允许定义的“线程局部变量”用于远程调用。
在 Visual Studio 中 T4 模板通常执行在单独的应用程序域中,即可以通过定期卸载应用程序域来释放内存。因此,远程的 CallContext 是指定模板参数值的好方法。下面的示例说明如何使用 CallContext 传递参数值。
CallContextTemplate.tt
<#@template debug="false" hostspecific="True" language="C#"#>
<#@output extension=".txt"#>
<#@import namespace="System.IO"#>
<#@import namespace="System.Runtime.Remoting.Messaging"#>
<#@import namespace="Microsoft.VisualStudio.TextTemplating"#>
<#
string templateFile = this.Host.ResolvePath("MyTemplate.tt");
string templateContent = File.ReadAllText(templateFile);
CallContext.LogicalSetData("MyParameter", "CallContextVall");
Engine engin = newEngine();
// 代码调用执行 文本模板 文件
stringgeneratedContent
= engin.ProcessTemplate(templateContent, this.Host);
CallContext.FreeNamedDataSlot("MyParameter");
this.Write(generatedContent);
#>
输出:
Parameter in statement block: CallContextVall
文本转换会话
在 Visual Studio 2010 中, T4 允许你像使用在 ASP.NET 中访问 Session 一样的方式访问 “文本转换会话”。文本转换会话允许你为特定模板定义全局变量。 会话变量能从当前的转换模板宿主传递到任意模板并且可以被用于模板参数值。
SessionTemplate.tt
<#@template debug="false" hostspecific="True" language="C#"#>
<#@output extension=".txt"#>
<#@Import Namespace="System.IO"#>
<#@Import Namespace="Microsoft.VisualStudio.TextTemplating"#>
<#
string templateFile = this.Host.ResolvePath("MyTemplate.tt");
string templateContent = File.ReadAllText(templateFile);
TextTemplatingSession session = newTextTemplatingSession();
session["MyParameter"] = "SessionValue";
var sessionHost = (ITextTemplatingSessionHost)this.Host;
sessionHost.Session = session;
Engine engine = newEngine();
string generatedContent = engine.ProcessTemplate(templateContent, this.Host);
this.Write(generatedContent);
#>
输出:
Parameter in statement block: SessionValue
在后台
T4 引擎为模板为每个参数指令生成一个私有的、只读的属性。自动生成的转换类类似如下代码:
MyTemplate.tt (运行时模板)
public partial class MyTemplate : TextTransformation
{
private string _MyParameterField;
private string MyParameter
{
get { return this._MyParameterField; } // 只读,私有属性
}
public override string TransformText()
{
this.GenerationEnvironment = null;
this.Write("\r\nParameter in statement block: ");
Write(MyParameter == null ? "null" : MyParameter);
return this.GenerationEnvironment.ToString();
}
// …
}
注意 MyParameter 属性是私有字段,这个字段被生成类中重写的 Initialize() 方法进行初始化,Initialize() 方法在 Transform() 方法之前被执行。
正如您所看到的该模板将首先尝试从 Session 字典中检索的参数值,如果Session无此键则会尝试 CallContext 。
作者: Oleg Sych
原文链接:http://www.olegsych.com/2010/05/t4-parameter-directive/