T4MVC介绍文档

英文地址:http://mvccontrib.codeplex.com/wikipage?title=T4MVC_doc&referringTitle=T4MVC

以下是翻译的文档,发现不正确或翻译不到位之处,欢迎指出。

1、介绍

T4MVC是为ASP.NET MVC应用程序创建的强类型T4模板,主要用于消除在使用Controllers、Actions和Views时的字符串。它有助于使你的MVC代码更容易维护,并且能给你在普通状态下输入代码不能提供的智能提示。T4MVC最初是David Ebbo在他博客文章上发表出来的,之后社区的反馈使其增添了许多新的功能。

T4MVC在Visual Studio 2008 SP1和Visual Studio 2010中均可以运行,并同时支持ASP.NET MVC 1.0和2.0。

只需简单地下载zip文件并且将T4MVC.tt和T4MVC.tt.settings.t4同时复制到您的MVC应用程序的根目录下。您应该可以马上看到在T4MVC.tt的作用下Visual Studio产生了一些文件。

这些生成的文件作用于整个应用程序,也就是这些脚本可以实现字符串的强类型替换。下面的文档将给出完整的脚本列表。

2、脚本

下面的文档不同于正式的说明文档,我们只是通过实例来演示在各种情况下T4MVC是如何提高你的代码质量。例子将通过前后的格式对比来指出变化之处。

注:下面给出的例子均是基于Nerd DinnerMVC应用程序。

2.1 使用View Name常量

任何时候,您会使用一个字符串来指向你的View,T4MVC将提供一个强类型的替代。例如,假设您有一个View的代码如下:

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

这么难看的“DinnerForm”字符串让它见鬼去吧!相反,您现在可以这样抒写:

<% Html.RenderPartial(MVC.Dinners.Views.DinnerForm);%>

当您需要使用一个Controller里面的View时,事情变得更加容易。例如,您可以将:

return View("InvalidOwner");

替换成:

return View(Views.InvalidOwner);

注意:因为代码已经在Dinner的controller里面了,所以您可以省略MVC.Dinners前缀。

2.2 关于Controller Actions

许多MVC的API都有这样一个模式,当你需要指向一个Controller Action时需要包含三个信息:

  1. Controller Name (经常会在当前Controller下隐藏Name)

  2. Action Name

  3. 传递给Action的参数

这些可以理解为字符串#1、#2和匿名对象#3。例如:

<%= Html.ActionLink("Delete Dinner", "Delete", "Dinners", new { id = Model.DinnerID }, null)%>

在这里,“Delete”和“Dinners”是我们要替换掉的的字符串。但是这还不是全部,因为“id”参数同样和其他两个字符串一样也需要替换掉。即使它看起来并不像一个字符串,这只不过是它的变相而已。可别让那些匿名对象欺骗了你。

有了T4MVC,相反,您应该这样写:

<%= Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID))%>

我们基本上替换掉了三个不必要的字符串(“Delete”,“Dinner”和“id”),取代他们的一个看起来比较舒服的方式来调用Controller Action。当然,这不是真的去调用Controller Action,这样是非常错误的。它调用方法的原理是将其转化为正确的路径。

这种方法的另外一个很大的好处是,当你在抒写代码时可以得到完整的智能代码提示。事实上,如果你使用类似ReShaper具有重构功能的工具,你甚至可以得到更多关于重构的支持。

添加额外的路径参数

在某些情况下,您可能需要将Action方法参数不存在的值添加到路径中。您可以这样做:

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
    .AddRouteValue("foo", 17))

您可以用这种方式很顺利的添加多个值。例如:

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
    .AddRouteValue("foo", 17)
    .AddRouteValue("bar", "abc"))

作为添加多个值的替代,您也可以这样写:

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
    .AddRouteValues(new { foo = 17, bar = "abc"}))

注意到在某些情况下,一些数据并非来自URL而是来自表单,您可能想在这样的路径中省略Action的参数。这时,您可以使用参数覆盖掉T4MVC生成路径参数。例如

Html.ActionLink("Delete Dinner", MVC.Dinners.Delete()
    .AddRouteValues(new { foo = 17, bar = "abc"}))

现在假设您要在所有T4MVC产生的代码中添加查询字符串,您可以使用AddRouteValues()重载需要的NameValueCollection。例如:

RedirectToAction(MVC.Home.MyAction().AddRouteValues(Request.QueryString));

这些都在现有的API中增加了一套 AddRouteValue / AddRouteValues方法,使其更容易应对各种不同的场景。

这些API使得我们在给路径添加额外的参数时不再需要重载所有的T4MVC helper了(例如上面提到的Html.ActionLink这种情况)。相反地,它会自动应用到任何使用T4MVC模式的方法里。

许多使用T4MVC的地方常常可以看见伪调用Controller Action的方式:

2.2.1 Html.ActionLink

Ok,关于Html.ActionLink的使用方法上面的例子已经涵盖了(这里就不在赘述)。

2.2.2 Url.Action
<%= Url.Action("Delete", "Dinners", new { id = Model.DinnerID })%>

变成:

<%= Url.Action(MVC.Dinners.Delete(Model.DinnerID))%>

2.2.3 Ajax.ActionLink

<%= Ajax.ActionLink( "RSVP for this event",
                     "Register", "RSVP",
                     new { id=Model.DinnerID }, 
                     new AjaxOptions { UpdateTargetId="rsvpmsg", OnSuccess="AnimateRSVPMessage" }) %>

变成:

<%= Ajax.ActionLink( "RSVP for this event",
                     MVC.RSVP.Register(Model.DinnerID),
                     new AjaxOptions { UpdateTargetId="rsvpmsg", OnSuccess="AnimateRSVPMessage" }) %>
2.2.4 RedirectToAction(Controller代码)
return RedirectToAction("Details", new { id = dinner.DinnerID });

变成:

return RedirectToAction(MVC.Dinners.Details(dinner.DinnerID));
2.2.5 routes.MapRoute(通常在Global.asax)
routes.MapRoute(
    "UpcomingDinners", 
    "Dinners/Page/{page}", 
    new { controller = "Dinners", action = "Index" }
);

变成:

routes.MapRoute(
    "UpcomingDinners", 
    "Dinners/Page/{page}", 
    MVC.Dinners.Index(null)
);
2.2.6 Html.BeginForm

从本质上讲,BeginForm()和ActionLink()的使用方法相同。但要注意,因为表单提交的数据通常都是来自于form,这一点不同于其它数据是来自URL参数。BeginForm()相比其他方法运用起来会比较复杂一些。

下面教你如何使用:

using (Html.BeginForm(MVC.Account.LogOn(), FormMethod.Post)) { ... }

如果您的Action方法有很多参数,您会在调用时提交这些参数。然而,当您这样做时,您要确保您在表单中提交时没有相同名字的参数名。(例如,使用一个文本框)。

一般来说,经验法则告诉我们当您的Action方法参数刚好和URL的一样时,Html.BeginForm()也能处理的相当好。

2.2.7 Html.RenderAction和Html.Action(只在MVC 2或更新版本)

Html.RenderAction和Html.Action对于MVC 2来说是新的东西。更多详细信息可以参考Phil Haack’s post

<%= Html.Action("Menu", new { options = new MenuOptions { Width=400, Height=500} })%>

变成:

<%= Html.Action(MVC.Home.Menu(new MenuOptions { Width=400, Height=500})); %>

Html.RenderAction同样也是这样使用。

2.3 强类型的脚本链接和静态资源

T4MVC会为您的静态内容和脚本文件生成一些静态的helper。因此,下面代码可以

<img src="/Content/nerd.jpg" />

替换成:

<img src="<%= Links.Content.nerd_jpg %>" />

同样的,脚本链接可以这样替换:

<script src="/Scripts/Map.js" type="text/javascript"></script>

替换成:

<script src="<%= Links.Scripts.Map_js %>" type="text/javascript"></script>

显然,这样做的一个好处是当您在移动或重命名静态资源后,可以尽早地在编译时就能发现这个错误(而不必等到部署之后才发现)。

另一个好处是,您可以得到一个更全面的参考。当您抒写 src=”/Content/nerd.jpg”,它只有在您的网站页面部署在根目录下才会有效。但是您使用了这个helper后,无论您的根目录在哪里,它在服务端执行一些判断后就能正确产生资源的路径。它是通过调用VirtualPathUtility.ToAbsolute(“~/Content/nerd.jpg”)来实现的。

不幸的是,有些原因致使VS在View中抒写代码时不能智能地提示。但有一种变通的方法,您可以在标签外部用智能提示的抒写后再复制到链接中。

重要说明:如果您看到您的<head>标签生成了奇怪的链接,您只需要删除runat=”server”即可。

支持压缩的Javascript文件

通常情况下你运用各种方法来压缩JavaScript文件,再去替换版本,这样做只是为了使它们尽可能的小,比如说去除JavaScript文件中的空白部分。比如在您的MVC应用程序的Script文件夹中通常可以看到两个文件存在jQuery.1.3.2.js和jQuery.1.3.2.min.js。在部署环境中,您通常想使用压缩后的版本,但是在开发环境下喜欢更容易调试的原版本。

T4MVC可以根据场景自动地使用一个版本。例如,假设您有:

<script src="<%= Links.Scripts.jquery_1_2_6_js %>" type="text/javascript"></script>

您不需要改变任何代码,T4MVC根据您正在运行Production环境来决定将做过标记的jquery_1_2_6_js自动指向jQuery.1.3.2.js或jQuery.1.3.2.min.js。它是如何知道您是否是在Production环境下呢?它是通过调用了一个在T4MVC.tt.settings.t4定义好的方法来判断的。在默认情况下,它会先判断下这个方法:

// Logic to determine if the app is running in production or dev environment
public static bool IsProduction() { 
    return (HttpContext.Current != null && !HttpContext.Current.IsDebuggingEnabled); 
}

如果您对“Production”有另外的环境定义,也可以很容易地改变这个判断。

2.4 在MVC 2 的“Areas”使用T4MVC

MVC 2中的主要新功能之一是将一个大型应用程序分解成许多的“Areas”。这种结构可以这样来描述:

T4MVC会自动作用于“Areas”并且使得他们同样存在于Model中。下面是一个例子:

<%= Html.ActionLink("Delete Dinner", MVC.NerdDinner.Dinners.Delete(Model.DinnerID))%>

请注意我们是如何引用MVC.NerdDinner.Dinners Controller的。如果您碰巧有一个和“Areas”名称一样的顶层Controller,这样的命名模式将会导致冲突。在这种情况下,T4MVC会在名字后面追加“Area”关键字来避免冲突。例如,如果您同时有一个Home Area和一个Home Controller(顶级的),您应该使用MVC.HomeArea.Hello来指向Area。

您可以在设置文件中可选地设置IncludeAreasToken为true。这样的命名方式就变成:

<%= Html.ActionLink("Delete Dinner", MVC.Areas.NerdDinner.Dinners.Delete(Model.DinnerID))%>

注意到在上面这种情况下,上述的冲突就不会发生了。但是这样做需要付出在更多的地方使用分段的代价。我也在考虑是否要保持这种方式,但是现在的默认方式已经很好了。

3、调整行为的T4MVC

当您下载T4MVC,您不仅获得了它主要的T4MVC.tt模板,同时您也获得了名为T4MVC.tt.settings.t4的文件。这个文件包含了T4MVC的各种设置,您可以用它来调整T4MVC生成的代码。本节将介绍它的各种设置:

Set AlwaysKeepTemplateDirty to false to turn on the always-dirty behavior
// If true, the template marks itself as unsaved as part of its execution.
// This way it will be saved and update itself next time the project is built.
// Basically, it keeps marking itself as unsaved to make the next build work.
// Note: this is certainly hacky, but is the best I could come up with so far.
bool AlwaysKeepTemplateDirty = true;
Set SplitIntoMultipleFiles to false to generate a single file with everything
// If true,the template output will be split into multiple files.
bool SplitIntoMultipleFiles = true;
Use HelpersPrefix to change the MVC prefix of the genrated classes
// The prefix used for things like MVC.Dinners.Name and MVC.Dinners.Delete(Model.DinnerID)
const string HelpersPrefix = "MVC";
Use ControllersFolder and ViewsRootFolder to rename the Controllers and Views folders
// The folder under the project that contains the controllers
const string ControllersFolder = "Controllers";

// The folder under the project that contains the views
const string ViewsRootFolder = "Views";
Use LinksNamespace the generated links’ namespace
// The namespace that the links are generated in (e.g. "Links", as in Links.Content.nerd_jpg)
const string LinksNamespace = "Links";
// Folders containing static files for which links are generated (e.g. Links.Scripts.Map_js)
readonly string[] StaticFilesFolders = new string[] {
    "Scripts",
    "Content",
};

4、相关资源

David Ebbo has written a series of blog posts on T4MVC
Scott Hanselman also blogged about it here

Last edited Aug 14 2011 at 4:09 PM by davidebbo, version 5

原文地址:http://mvccontrib.codeplex.com/wikipage?title=T4MVC_doc&referringTitle=T4MVC