登峰造极的 ASP.NET

文本模板转换工具包和 ASP.NET MVC

K. Scott Allen

Microsoft Visual Studio 包含一个称为 T4(文本模板转换工具包的缩写)的代码生成引擎。您或许已在 Visual Studio 中使用 T4 模板,甚至还不知道这些模板已在后台工作。在本文中,我将简单介绍 T4 模板并说明 ASP.NET MVC 如何使用此项技术。同时我还会为您介绍如何自定义 T4 模板以使用 MVC 框架提高您的日常工作效率。

该模板工具包背后的基本理念是分析输入文件并将其转换为输出文件。输入文件是一个模板(文件扩展名为 .tt 的文本文件)。输出文件还会包含文本,文本可以是 C# 代码、Visual Basic 代码、Web 窗体代码、标记或您需要生成的任何其他内容。

观看 T4 演示的最简单方法是在 Visual Studio 中创建一个新项目。我将在本文中生成 C# 代码,因此您可以使用编译 C# 代码的任何项目类型。打开项目后,右键单击该项目,然后选择“添加”|“新建项目”。从“添加新项”对话框中选择“文本文件”(Visual Studio 2008 中没有专用于 T4 的项目模板,但 Visual Studio 2010 中将提供),然后将文件命名为 Simple.tt(确保使用 .tt 扩展名)。将该文件加载到项目后,您会立即在“解决方案资源管理器”窗口中看到 Simple.cs 文件显示在 Simple.tt 之后。(请参见图 1)。

图 1 T4 模板后面的 C# 文件

image: C# File Behind a T4 Template

Simple.tt 和 Simple.cs 文件一开始都是空文件。如果您右键单击 Simple.tt 文件,然后选择“属性”,您将看到 Visual Studio 将 TextTemplatingFileGenerator 指定为文件的自定义工具(请参见图 2)。此生成器是 T4 引擎,会将模板文件转换为一个完全由 C# 代码组成的文件。

图 2 T4 模板的属性

image: Properties of the T4 Template

若要使该模板执行所需操作,请添加以下代码:

<#@ template language="c#v3.5" #>
<#@ assembly name="System.Web.Mvc.DLL" #>
<#@ import namespace="System.Web.Mvc" #>

public class Test
{
<# for(int i = 0; i < 5; i++) { #> 
  public int Prop<#= i #> { get; set; }
<# } #>
}

该代码以某些指令开始。这些指令允许您指定该模板的编程语言并将代码所需的命名空间和程序集包含在该模板内。我想强调的是我所谈论的是执行模板中的代码所需的设置,而不是项目本身包含的代码。您还可以使用指令指定输出文件的扩展名。默认的是 C#,但正如我之前提到的,您可以生成 Visual Basic 代码、XML、HTML 或任何其他文本项目。

我正在使用的指令指示模板引擎使用随 Microsoft .NET Framework 3.5 一同提供的 C# 编译器。它还指示模板引擎引用 ASP.NET MVC 程序集并将 System.Web.Mvc 命名空间引入作用域。MVC 程序集和命名空间不是模板中的简单代码实际需要的,但是我把它们作为一个示例放在模板中。

在指令后面,您看到的不在 <# 和 #> 分隔符之间的文本被逐字放到输出文件中。位于 <# 和 #> 之间的文本是 C# 代码。模板引擎将分析该代码,然后将其添加到类以便执行(最终由 Microsoft.VisualStudio.TextTemplating 程序集中的 TextTransformation 类派生的类)。此过程类似于 ASP.NET 视图引擎过程,在该过程中将 .aspx 文件中的代码和标记添加到最终由 System.Web.UI.Page 派生的类。如果您已使用 Web 窗体视图引擎编写您的 MVC 视图,则创建模板将得心应手。在 .aspx 文件中,您可以使用 C# 代码生成 HTML。在 .tt 文件中,我使用 C# 代码生成 C# 代码。

Simple.tt 中的代码将在 Simple.tt.cs 中生成以下 C# 输出:

public class Test
{
  public int Prop0 { get; set; }
  public int Prop1 { get; set; }
  public int Prop2 { get; set; }
  public int Prop3 { get; set; }
  public int Prop4 { get; set; }
}

当然,Test 类毫无用处且完全没有意义,但我希望它能帮助您了解 T4 模板所具备的某些功能。由于您正在编写模板中的 C# 代码,因此您可以连接到数据库、读取文件系统中的数据、分析 XML 或使用任何 .NET 类连接或读取存在于您环境中的元数据。此元数据(类似于数据库架构或其他程序集内部的类型)是您可以用于生成类的信息。这些类将成为当前项目的一部分,因此他们将编译到当前程序集并且您可以在您的应用程序中使用这些类。

T4 编辑

当您编辑 Visual Studio 中的 T4 模板时,IDE 中的语言服务(例如 IntelliSense 和突出显示的语法)对您没有任何帮助。有两种解决方案可以解决这种问题。一种是 Clarius Consulting 提供的 Visual T4 编辑器 (visualt4.com)。另一种解决方案是 Tangible Engineering 提供的 Tangible T4 编辑器 (t4-editor.tangible-engineering.com)。

对 T4 模板的工作方式有了基本了解后,让我们来看一下 MVC 框架使用 T4 模板的方式。

ASP.NET MVC 中的 T4

每次使用“添加视图”或“添加控制器”功能时,您都在 ASP.NET MVC 项目中使用 T4 模板。这些模板位于 Common7\IDE\ItemTemplates\CSharp\Web\MVC 2\CodeTemplates 文件夹的 Visual Studio 安装中。另外还提供了模板的 Visual Basic 版本,但是我将其作为读者推断文件夹名称的一个练习。

这些模板本身足以说明 T4 的价值和功能。例如,以下是 CodeTemplates 的 AddView 子文件夹中 List.tt 的一段摘录:

  if(!String.IsNullOrEmpty(mvcViewDataTypeGenericString)) {
    Dictionary<string, string> properties = 
      new Dictionary<string, string>();
    FilterProperties(mvcHost.ViewDataType, properties);
  #>
    <table>
      <tr>
        <th></th>
  <#
    foreach(KeyValuePair<string, string> property in properties) {
  #>
        <th>
          <#= property.Key #>
        </th>
  <#
    }
  #>

List.tt 的工作是生成将以表格形式显示模型对象集合的 .aspx 文件。在该模板中,您可以看到编写的表、tr 和 th 标记。若要生成 .aspx 文件,该模板需要一些上下文信息,例如,应使用的母版页的名称和模型的类型。该模板可以从其宿主对象检索此信息。该宿主对象位于模板和 T4 引擎之间,并可以使模板访问资源(如本地文件)和环境设置。通常,宿主是 Visual Studio,但 MVC 团队在 Microsoft.VisualStudio.Web.Extensions 程序集中创建了类型为 MvcTextTemplateHost 的自定义宿主。这便是沿用了您在“添加视图”和“添加控制器”对话框中输入的信息的自定义宿主对象,这些对话框是您将发现的最接近 MVC 项目中的向导的事物。

List.tt 将遍历强类型化模型对象的可显示属性并创建具有每个属性列的表。该模板使用反射在 FilterProperties 方法中查找该模型的可用属性。FilterProperties 是稍后在模板文件中定义的帮助器方法。该模板还设置了导航到编辑和详细信息操作的链接,并根据您正在创建视图还是部分视图来为 .aspx 设置适当的 @ Page 或 @ Control 指令。  

当模板结束运行时,您将看到一个崭新的 .aspx 视图,该视图包含显示模型对象集合所需的所有内容。您可能会打开 .aspx 文件并进行微调以使该视图在外观上与应用程序其余部分中的视图保持一致。

如果您发现总是对这些生成的视图(或 Controller.tt 生成的控制器代码)进行相同的更改,则可以通过修改模板本身来节省时间。例如,您可以修改内置模板,以便为项目中使用的样式规则甚至更重要的内容添加类属性。请记住,修改 Visual Studio 安装目录中的模板文件将更改在您的计算机上进行的所有项目中生成的代码。如果您希望更改为单个项目生成的代码,您也可以这样做。

各个项目 T4 自定义

如果您想要基于各个项目的代码生成模板的自定义版本,则首先复制 Visual Studio 安装中的 CodeTemplates 文件夹,然后将其粘贴到您的 ASP.NET MVC 项目的根目录中。然而,不必将所有模板都复制到您的项目中。您可以只复制需要修改的模板。共有 6 个 MVC 代码生成模板,1 个用于添加控制器 (Controller.tt),其余 5 个用于添加视图(Create.tt、Details.tt、Edit.tt、Empty.tt、List.tt)。如果您的项目中存在模板,则该模板将覆盖 Visual Studio 安装目录中的模板。

当向 Visual Studio 解决方案中添加 .tt 文件时,IDE 会自动将该 .tt 文件分配给 TextTemplatingFileGenerator 的自定义工具设置。如果您创建了我之前所述的 Simple.tt 模板,则您已看到这种情况。但是,这不是 MVC T4 模板的适当设置。Visual Studio 的 MVC 工具将在适当的时候调用这些模板并在模板处理期间创建特殊的 MvcTextTemplateHost 对象。因此,将模板复制到您的项目中后,第二步是为各个模板文件打开“属性”窗口,然后删除“自定义工具”设置(保留该设置为空)。此时,便可以自定义您的模板了。

MvcTextTemplateHost 属性
请注意,并非 MvcTextTemplateHost 对象上的所有属性都适用于每个上下文。这些模板将在您调用“添加视图”和“添加控制器”上下文菜单项时得以执行。这两种操作均可使用 Namespace 属性并将其设置为适当的值。但是,MasterPage 属性在“添加视图”操作期间仅被设置为有效值并包含用户在“添加视图”对话框中为 MasterPage 名称输入的值。

例如,假设您不希望控制器执行索引操作。而更希望使用默认操作 List。您可以打开 CodeTemplates\AddController 文件夹中的 Controller.tt 模板,并将代码的相应区域更改为如下内容:

public class <#= mvcHost.ControllerName #> : Controller
{
  // GET: /<#= mvcHost.ControllerRootName #>/

  public ActionResult List()
  {
    return View();
  }
...

这是一个简单的更改,但可为您和您的团队在整个大型项目生命周期内节省相当多的时间。

更进一步—T4MVC

2009 年夏天,ASP.NET 团队的 David Ebbo 创建了 T4MVC,一种 T4 模板,专用于在 ASP.NET MVC 应用程序中生成强类型化帮助器。随着时间的推移,Ebbo 已对该模板进行了完善,您现在可以从 aspnet.codeplex.com/wikipage?title=T4mvc 下载该模板。

T4MVC 模板是传统的 T4 模板。将 T4MVC.tt 及其关联的设置文件 (T4MVC.settings.t4) 添加到您的项目,该项目将使用 TextTemplatingFileGenerator 自定义工具生成 C# 代码。T4MVC 将帮助您消除 MVC 应用程序中的许多奇妙的字符串文本。例如,模板将执行的一项任务是扫描项目中的 Content 和 Scripts 文件夹并生成具有静态属性的类以表示各个脚本和内容片段。

生成的代码意味着您可以呈现由带有该代码的默认 MVC 项目提供的 LogOnUserControl 部分视图:

<% Html.RenderPartial(MVC.Shared.Views.LogOnUserControl); %>

之前,您可能已使用了字符串文本:

<% Html.RenderPartial("LogOnUserControl"); %>

如果有人重命名、移动或删除 LogonUserControl,则强类型化代码将在编译视图时生成编译错误。除了对视图和部分视图的强类型化访问外,T4MVC 模板还提供了对 Content 和 Scripts 文件夹中的所有文件以及控制器和控制器操作的强类型化访问。

您可以在建立操作链接、返回查看结果,甚至在为应用程序构建路由表时使用 T4MVC 生成的类。请注意,当首次向项目中添加 T4MVC 时,您将看到在 IDE 的“错误列表”窗口中生成的一些警告。这些警告只是 T4MVC 向您介绍应用到代码的某些更改。其中大部分更改不会改变应用程序的行为;T4MVC 模板只向控制器类定义中添加某些局部关键字并使非虚拟操作方法变为虚拟方法。有关 T4MVC 的详细信息,请查看 Ebbo 的博客,网址为 blogs.msdn.com/davidebb

更易于维护

T4 是 Visual Studio 中非常神奇的财富,但还不是广为人知的模板。本文为您提供了在 ASP.NET MVC 项目中开始使用自定义模板所需的一切内容。希望您还能发现 T4 模板在 Web 应用程序项目之外的其他用途。您还应该在项目中试用 T4MVC 模板,因为这些模板可使您的代码更易于维护和重构。展望未来,Visual Studio 2010 中的 T4 技术将添加专用的项目模板和预编译模板,从而更趋完善。

K. Scott Allen 是 OdeToCode 的首席顾问和创始人,也是 Pluralsight 技术团队的成员。您可以通过他的博客 (odetocode.com/blogs/scott) 或通过 Twitter (twitter.com/OdeToCode) 与 Allen 联系。

衷心感谢以下技术专家审阅本文:David Ebbo