最近由于需要在框架中提供一些自定义模板的功能,找到了一篇博客,可惜似乎是翻译工具直接翻的,读不通顺,就试着自己翻译下,我不会完全翻译原文的句子,可能会对原文进行小范围的我认为更合适的句子并添加些注释,,原文地址如下:
http://blogs.msdn.com/b/webdev/archive/2009/01/29/t4-templates-a-quick-start-guide-for-asp-net-mvc-developers.aspx
在我们最近的博客中提到的ASP.NET MVC Release Candidate中,我们使用了T4 (Text Template Transformation Toolkit)模板来实现了Add Controller和Add View的代码自动生成功能。我们希望能使用T4给大家带来开发速度上的提升,因为用户可以在很大程度上定制这些模板。
模板的位置和重写
Add Controller和Add View都是通过在后台执行T4模板来实现代码自动生成的。通过一些工具可以修改模板来定制想要生成的代码。默认模板的位置在: [Visual Studio Install Directory]\Common7\IDE\ItemTemplates\[CSharp | VisualBasic]\Web\MVC\CodeTemplates\
你也可以把“CodeTemplates”目录复制到项目的根目录以便作为每一个项目的基础在上述位置重写和定制模板(只需要创建目录名为“CodeTemplates”的目录,并在其下创建“AddController”和“AddView”的子目录即可)。需要默认目录下有一些模板的重写可能不起作用,因为自动生成代码时将会优先选取项目目录中匹配的模板。还有一个重点需要注意,你可以为Add View功能增加自己的.tt 文件,无论是在公用目录下还是独立的项目中,添加后都可以自动的显示在Add View窗口的视图选择的下拉框中。
另外,复制上述文件夹(添加.tt文件)到项目中时,都会有警告提示,如下图:
(运行这些文本模板可能会对你的电脑产生危害。如果模板来源不可信请不要运行。)
点击“Cancel”将不能使用T4模板(如果你复制“CodeTemplates”目录并增加多个.tt文件时,你都选择了“Cancel”)。因为一旦项目发现一个.tt文件,文件的“自定义工具(CustomTool①)”属性将被设置成“TextTemplatingFileGeneraror”,它会通知Visual Studio使用默认的T4宿主(Host②)去执行这个模板并且创建一个基于此模板内容的新的文件(在模板所在的目录)。
① :自定义工具是一个实现了 Visual Studio 定义的IVsSingleFileGenerator接口的特殊组件。T4 自定义工具由Microsoft.VisualStudio.TextTemplating.VSHost.TemplatedCodeGenerator类实现并且在注册表中注册在Visual Studio 下命名为TextTemplatingFileGenerator。Visual Studio 将使用该名称查找自定义工具、创建 COM 对象并调用其IVsSingleFileGenerator.Generate 方法
② :Microsoft.VisualStudio.TextTemplating.VSHost.10.0.dll 的TextTemplatingService服务实现Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost接口,为代码生成引擎提供 Host (宿主)。在模板转换过程中: Engine 负责代码生成;Host负责提供转换过程中行为具体实现及所需要的的所有外部资源,ITextTemplatingEngineHost和模板内容一起传递到Engine.ProcessTempalte方法中用于代码生成
对于基于模板的一次性文件生成或简单的使用T4,生成器都可以作为好的的实现方式---但是,因为依赖于自定义模板的宿主的Add View 和Add Controller模板都包含代码(后面会提到),使用默认的生成器执行模板将会生成错误。需要复制模板到项目中之后然后清空自定义工具属性值。
注意:如果你想去掉复制.tt文件时自动设置自定义工具的映射值,你可以通过注册表实现---注意如果你想要恢复注册表为默认值,你可以执行Visual Studio的恢复安装,或手动改回。启动注册表编辑器并且找到指定的目录(根据你的机器选择是32位还是64位):
32位:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators
64位:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\9.0\Generators
展开“Generators”下第一层的每一个节点并找到“.tt”。设置“(Default)”项的值为空,就可以了。
如果你想重写全局模板,并且不想复制“CodeTemplates”目录到你的项目(大概是你已经有了个同名目录),你可以通过注册表修改Add Controller 和Add View默认的目录名。启动注册表编辑器并且找到指定的目录(根据你的机器选择是32位还是64位):
32-bit: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\MVC\CodeTemplates
64-bit: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\9.0\MVC\CodeTemplates
改变“OverrideDir”项的值为你想要T4工具检索的目录名。需要注意的是,新设置的目录下依然需要保证与原来相同的目录结构,即包含“AddController”和“AddView”目录。
编辑T4模板
如果你使用Visual Studio打开一个.tt文件,你可能会发现它的内容没有层次---平铺在上面。我们强烈推荐你使用T4编辑器,一个Clarius Consulting开发的Visual Studio插件,它可以在你编辑模板时提供语法高亮和一些基本的T4语句的自动完成。有免费的“Community Edition”,以及更强大的“Professional Edition”---如果你有兴趣可以比较下不同的特性。下面你将会看到,这个插件真的让模板制作有了很大不同。
深入剖析T4模板
最简单的学习编辑自定义模板的方法是先看默认模板如何工作。为此,我们通过一些Add View的模板来了解“Create”(创建.tt文件)。现在从头开始:
这四行代码都是指令。
1.第一行是模板指令,它的主要作用是通知T4引擎,模板使用哪种语言编写。这里所说的模板使用的语言并不是模板会输出这种语言,而这种语言会控制模板的输出(例如,模板中可能会报刊一个 if-else语句用来控制哪些文本被输出)。在使用Add View 或者Add Controller之时,“HostSpecific”属性需要设置为“True”,否则模板将不能访问Add View 和Add Controller功能提供的信息(比如将类型绑定为View的强类型,等等)。
2.紧接着的是输出指令,它简单的通过通知模板宿主来设置模板输出文件的扩展名---这不是MVC特有的工具,因为根据不同情况可能会忽略默认的扩展名(例如,如果我们依赖partial view ,扩展名应该是.ascx)。
3.接下来的两行都是引用指令,C#中通常使用“using”,VB中使用“Imports”。如果你在模板中使用了用它们所写的代码,你需要在这个位置引用它们。
你可以在这里找到更多T4指令的文档:http://msdn.microsoft.com/en-us/library/bb126421.aspx
下面一点,注意一下后面的这行:
这行我们新建了一个变量“mvcHost”,将“Host”转换类型并赋值给它。因为我们指令中设置了“HostSpecific”属性为“True”,“Host”被自动提供给模板。MVC工具提供了一个自定义宿主,以便能将信息传递给只有工具内部可访问的模板。为了访问我们的宿主类中提供的这些属性,“Host”必须转换为我们自定义的宿主类型“MvcTextTemplateHost”。
这个变量的使用实际上非常简单:
首先需要注意的是,‘<#’ 和 ‘#>’标签之间包含有一些的代码。这两个标签用来调用语句块和包含控制代码。你的模板可能要有条件的输出文本块到输出文件中,同时保持其他文本块的正常输出。上面,我们有一个if语句(使用C#,因为模板的语言使用模板指令设置成了C#),使用了“{”。这个if语句的“}”出现在后面一些行中的的第17行,在不同的语句块中。注意,这里的这个if语句访问了宿主的“IsViewUserControl”属性,这个属性的作用是通知模板用户选择的是否是partial view。
模板中所有这些标记之外的文本块都会直接输出到实际的文件中。在上面的截图中,第14行的文本在标记块之外,因此会直接输出---尽管如此,它只会在12行的if语句值判断为“true”的情况下才会输出到文件中。
你可以在这里了解到更多关于T4语句块的信息:http://msdn.microsoft.com/en-us/library/bb126509.aspx
理解T4模板中控制代码的最简单的方法是在大脑中将它连接为一个完整的程序。我们之前在第6行声明的变量可以被之后的T4控制代码使用---之后每一个if-else分支条件都可以使用这个变量并且添加一些文本(灰色的)到输出的文件中。上面的If-else语句选择一些由模板宿主提供的属性传递给Add View模板。
后面一些行中有一个奇怪的语句块:
我们声明了一个“List<string>”类型的变量。我们怎么能在T4代码中使用List类型的?因为可以看到我们在模板顶部的引用指令:
第56行调用了一个方法“FilterProperties”---但是方法在哪?实际上这个方法的定义在模板底部:
如果你仔细看,这个语句块有点不一样,是以“<#+”开始的。这是一个用来表示类的块,它们这样工作:T4模板带有所有的类的特征,并且将这些特征添加到在模板的后台编译的类中。这不只限于方法,还包括属性(通常包含在类中的)。像一个正规代码文件中的类成员一样,它们可以被模板中的其他代码访问。
关于类特征块的更多信息:http://msdn.microsoft.com/en-us/library/bb126541.aspx
我们的默认模板使用上面截图中的“FilterProperties”方法在默认输出中的type包含的标记过滤获得一些properties,这些标记必须是公共的和像GirdView一样可以在设计器中显示的。这与调用GridView .IsBindableType方法的结果在逻辑上是相同的。
(这段翻译的很别扭,所以附上原文,请大家帮忙指正)
Our default templates use the ‘FilterProperties’ method from the above screenshot to by default output markup only for some properties in the type – namely ones that are public and would also be displayed in the designer by things like GridView. This logic is identical to what you would get from calling the GridView.IsBindableType method.
在136行调用的“IsBindableType”是在模板后面一点位置被定义的:
如果你想改变我们模板过滤掉的某个属性,你可以按照你的喜好修改任意一个方法。
最后,看看第65行:
这里有一个新的标记开头“<#=”。它被一个表达式块调用并且用来插入T4代码的值到输出文本。像你在上面看到的,我们有一个foreach循环,迭代properties 并定义一个局部变量来循环调用“propertyName”。我们使用表达式块引用该变量,因为我们想为每一个属性名输出一个<th>标签。
更多关于表达式块:http://msdn.microsoft.com/en-us/library/bb126508.aspx
如你所见,上面各种使用T4模板的方法通过选择性的输出文本使T4成为一个强大的工具,帮助我们获得灵活的自定义的自动代码生成。
MVC工具的T4宿主属性
Add Controller 和Add View功能都通过模板宿主提供不同的属性给模板,上述说明贯穿了创建模板的过程。这里完整的列出了模板宿主的有效属性,供你的模板使用(注意:这些属性的实际名称可能会因为不同的版本有所不同)。
Property Name |
Type |
Description |
ItemName |
System.String |
带有“Controller”后缀的Controller类的名称 |
NameSpace |
System.String |
生成的Controller类的命名空间 |
ExtraActionMethods |
System.Boolean |
指示用户是否选择在Add Controller窗口中扩展Action方法 |
ControllerRootName |
System.String |
不带“Controller’”后缀的Controller类的名称 |
Property Name |
Type |
Description |
ItemName |
System.String |
不带扩展名的View名称,例如显示于Add View窗口中的 |
NameSpace |
System.String |
默认以View所在的目录作为命名空间 |
IsViewUserControl |
System.Boolean |
在Add View窗口中用户是否选择了partial view |
IsViewContentPage |
System.Boolean |
用户创建View时是否带有master page |
IsViewPage |
System.Boolean |
用户创建的是否是符合规范的视图页 |
MasterPage |
System.String |
用户在窗口中选择的master page路径 (只能在 IsViewContentPage是true的时候使用) |
ContentPlaceholder |
System.String |
生成内容所在位置的占位符名称。是用户输入到Add View 窗口中内容占位符ID |
ContentPlaceHolders |
System.Collections.Generic.List<System.String> |
master page中所有内容占位符ID的列表, 如果View选择了master page |
LanguageExtension |
System.String |
输出文件的扩展名 (包含”.”) |
ViewDataTypeGenericString |
System.String |
在View指令中用来输出泛型”Inherits”属性的字符串(强类型 view). 例如: “<MyType>” 或 “(Of MyType)” |
ViewDataType |
System.Type |
被绑定的强类型View的Type对象。可以获得类型的属性信息等 |
此外,你会发现一些提供给宿主的用用的属性,比如正在执行的模板路径。更多关于这些属性: http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.texttemplating.itexttemplatingenginehost_properties.aspx
我们希望这篇文章带给了你足够关于T4模板的信息,能帮你有效的结合Visual Web Developer provides for ASP.NET MVC工具。你如果想知道这篇博客之外更多的信息,可以看看那的信息(Scott Hanselman’s博客不错)并看看你能得到什么。请回复您的评论,建议或问题。谢谢您的阅读!