T4MVC Documentation page(转)

转自:http://mvccontrib.codeplex.com/wikipage?title=T4MVC_doc&referringTitle=T4MVC

1. Introduction

T4MVC is a T4 template for ASP.NET MVC apps that creates strongly typed helpers that eliminate the use of literal strings when referring the controllers, actions and views. It helps make your MVC code much more maintainable, and gives you intellisense where you normally would not have any. It was started by David Ebbo as a little blog post thing, and has since gained many new features based on great community feedback.

T4MVC runs in both Visual Studio 2008 SP1 and Visual Studio 2010, and supports both ASP.NET MVC 1.0 and 2.0.

To use it, simply download the zip file, and copy both T4MVC.tt and T4MVC.tt.settings.t4 to the root of your MVC application. You should instantly see a number of files get created in Visual Studio under T4MVC.tt.

The code in those generated files enable a whole range of scenarios, all relating to replacing unwanted literal strings with a strongly typed alternative. The documentation below gives a complete list of those scenarios.

2. Scenarios

Instead of a formal documentation, we'll just demonstrate by example the various scenarios where T4MVC helps improve your code. The examples are shown in a before/after format to outline the change that is made.

Note: a lot of the examples given below are based on the Nerd Dinner sample MVC application. 

2.1 Using View Name constants


Any time you would use a literal string to refer to a view, T4MVC gives you a strongly typed alternative. e.g. suppose you have this code in a view:

<% Html.RenderPartial("DinnerForm"); %>
This ugly “DinnerForm” literal string needs to go! Instead, you can now write:

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

When you need to use a view name inside a controller, things get even easier. e.g. you can replace:

return View("InvalidOwner");
by

return View(Views.InvalidOwner);

Note how you can omit the MVC.Dinners prefix, since this is implied by the fact that the code lives in the Dinners controller.

2.2 Referring to controller actions


Many MVC APIs follow a pattern where you need to pass three pieces of information in order to refer to a controller action:
  1. the controller name (often implied when dealing with the current controller)
  2. the action method name
  3. the parameters to be passed to the action method

All of this is typically done by passing literal strings for #1 and #2, and an anonymous object for #3, e.g.

<%= Html.ActionLink("Delete Dinner", "Delete", "Dinners", new { id = Model.DinnerID }, null)%>
Here, "Delete" and "Dinners" are clear literal strings that we'd like to get rid of. But that's not all, as the paramter name 'id' is as much of a problem as those other two. Even though it doesn’t look like a literal string, it very much is one in disguise. Don’t let those anonymous objects fool you!

With T4MVC, you would instead write:

<%= Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID))%>
Basically, we got rid of the three unwanted literal strings (“Delete”, "Dinners" and “Id”), and replaced them by a very natural looking method call to the controller action. Of course, this is not really calling the controller action, which would be very wrong here. But it’s capturing the essence of method call, and turning it into the right route values.

One other great benefit of this approach is that you get full intellisense while you type this line. In fact, you even get full refactoring support if you use a tool like resharper which supports aspx refactoring!

Adding additional route parameters

In some situation, you may need to add extra values to the route that don’t exist in the action method. You can do it as follows:

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

You can add multiple values this way using a fluent approach, e.g.

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

As an alternative for adding multiple values, you can write:

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

Note that in some cases, you may want to omit the action method parameters from the route, possibly because they are meant to come from form data and not from the URL. For those cases, you can always use the parameter-less override that T4MVC generates (that part is not new), e.g.

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

Now suppose you want to add all the current query string values to the route values produced by T4MVC. You can use the AddRouteValues() overload that takes a NameValueCollection. e.g.

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

This adds to the existing set of AddRouteValue/AddRouteValues fluent APIs to make it easier to deal with all kind different situations.

The nice thing about this fluent approach is that it doesn’t require adding overloads to all the T4MVC helpers (e.g. Html.ActionLink is this case) to take in the extra route values. Instead, it automatically applies to any method that uses the T4MVC pattern.

The general pattern of making a pseudo-call to a controller action is used is many places in T4MVC:

2.2.1 Html.ActionLink

Ok, we just covered that one above.

2.2.2 Url.Action

<%= Url.Action("Delete", "Dinners", new { id = Model.DinnerID })%>
becomeshttp://mvccontrib.codeplex.com/wikipage?action=Edit&title=T4MVC_doc&version=2&referringTitle=T4MVC

<%= 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" }) %>
becomes

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

2.2.4 RedirectToAction (in controller code)

return RedirectToAction("Details", new { id = dinner.DinnerID });
becomes

return RedirectToAction(MVC.Dinners.Details(dinner.DinnerID));

2.2.5 routes.MapRoute (typically in global.asax)

routes.MapRoute(
    "UpcomingDinners", 
    "Dinners/Page/{page}", 
    new { controller = "Dinners", action = "Index" }
);
becomes

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

2.2.6 Html.BeginForm

Essentially, it’s the same support as ActionLink() but for the BeginForm() method. But note that because form posts typically pass most of the data via the form and not the URL, BeginForm() is trickier to use correctly than the other methods.

Here is how you might use this:

using (Html.BeginForm(MVC.Account.LogOn(), FormMethod.Post)) { ... }
Or if the action method takes parameters, you would pass them in the call. However, when you do this you need to make sure that you aren’t also trying to pass those same parameters via the form (e.g. using a text box).

Generally, the rule of thumb is that this support works well when you’re dealing with forms where the Action method signature exactly matches the form URL.

2.2.7 Html.RenderAction and Html.Action (only in MVC 2 or newer)

These are new to MVC 2. See Phil Haack's post about them for details.

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

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

2.3. Strongly typed links to script files and static resources


T4MVC generates static helpers for your content files and script files. So instead of writing:

<img src="/Content/nerd.jpg" />
You can write:

<img src="<%= Links.Content.nerd_jpg %>" />
Likewise, instead of

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

<script src="<%= Links.Scripts.Map_js %>" type="text/javascript"></script>
The obvious benefit is that you’ll get a compile error if you ever move or rename your static resource, so you’ll catch it earlier.

Another benefit is that you get a more versatile reference. When you write src="/Content/nerd.jpg", your app will only work when it’s deployed at the root of the site. But when you use the helper, it executes some server side logic that makes sure your reference is correct wherever your site is rooted. It does this by calling VirtualPathUtility.ToAbsolute("~/Content/nerd.jpg").

One unfortunate thing is that for some reason, VS doesn’t support intellisense in the view for parameter values. As a workaround, you can type it outside of the tag to get intellisense and then copy it there.

Important note: if you see strange links getting generated in your <head> tag, you just need to remove the runat="server" from it.

Support for Minified Javascript files

Minified javascript files are alternate version of the files that makes them as small as possible, but using various strategies like removing white space. e.g. in an MVC application’s Scripts folder, you’ll typically see both jquery-1.3.2.js and jquery-1.3.2.min.js. Usually, you want to use the minified version at deployment time (to make your site load faster), but the regular version at development time, so you can more easily debug into it.

T4MVC makes it automatic to use one vs. the other depending on the context. e.g. suppose you have:

<script src="<%= Links.Scripts.jquery_1_2_6_js %>" type="text/javascript"></script>
You don’t need to change anything to this line, but the token jquery_1_2_6_js will automatically point to either jquery-1.3.2.js or jquery-1.3.2.min.js depending on whether you’re running in production. How does it know whether you’re in production? It calls a method defined in T4MVC.tt.settings.t4 which makes the decision. By default, it looks at whether debugging is enabled:

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

But you can easily change this logic if you have a different way of determining ‘production’.

2.4 Using T4MVC with MVC 2 "Areas"


One of MVC 2's major new features is the support for breaking up a large application into "Areas". This works by following a structure that looks like:
  • Root folder
    • Areas
      • NerdDinner
        • Models
        • Views
        • Controllers
      • Wiki
        • Models
        • Views
        • Controllers

T4MVC automatically works with your areas and makes them available in its object model. Here is an example:

<%= Html.ActionLink("Delete Dinner", MVC.NerdDinner.Dinners.Delete(Model.DinnerID))%>
Notice how we refered to the controller as MVC.NerdDinner.Dinners. Note that if you happen to have a top level controller with the same name as the area, this naming pattern would cause a conflict. In that case, T4MVC appends 'Area' at the end of the area name to avoid the conflict. e.g. If you have both a Home area and a Home controller (top level), you would use MVC.HomeArea.Hello to refer to the area.

Optionally, if you set IncludeAreasToken to true in the settings file, the naming scheme becomes:

<%= Html.ActionLink("Delete Dinner", MVC.Areas.NerdDinner.Dinners.Delete(Model.DinnerID))%>
Note that in this case, the conflict situation discussed above cannot occur. But this comes with a price as you end up with one more segment all the time. I'm debating whether to even keep this mode given that the default mode works quite well.

3. Tweaking the behavior of T4MVC

When you download T4MVC, you not only get the main T4MVC.tt template, but you also get a file named T4MVC.tt.settings.t4. This file contains various knobs that you can use to tweak the code generated by T4MVC. This section describes the various switches:

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";

Use StaticFilesFolders to tweak the list of folders from which links are generated

// Folders containing static files for which links are generated (e.g. Links.Scripts.Map_js)
readonly string[] StaticFilesFolders = new string[] {
    "Scripts",
    "Content",
};

4. Related resources

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
posted @ 2012-02-27 11:15  WEBBER  阅读(419)  评论(0编辑  收藏  举报