Archive for the ‘T4’ Category

A better way to auto-run T4MVC when you build

T4MVC has always struggled with finding the right way of running automatically when you build.  Being a T4 template in your project, by default it just runs when you save it.  This is not great, because you really want it to rerun whenever you make a change that affects it: e.g. new controller action, new static file, …

So I had come up with a pretty crazy workaround (the AlwaysKeepTemplateDirty flag), described in details in this previous post (under “The T4 file automatically runs whenever you build”).  It’s a pretty hacky technique, which mostly works but has some nasty quirks:

  • You need to open and save it once to start the save ‘cycle’
  • You need to leave it open
  • It’s always marked as dirty, which is an annoyance.  The little ‘dirty star’ is always there, and when you close the project you’re always prompted if you want to save it, even though you didn’t change it.

Recently, Danish developer Joachim Lykke Andersen asked a question about this on StackOverflow, and then proceeded to find a pretty nice solution to it himself, which he has now blogged.  His solution relies on handling the VS OnBuildBegin event.  So basically, you just do a one time step, and then you don’t need to worry about it.  I won’t repeat all the details here, so get them on his post.

He also discusses the possibility off turning this into an Addin, which would be yet cooler.  And maybe with some convincing he will, so add a comment to his post :)

 

POLL: should we kill the AlwaysKeepTemplateDirty flag?

Given that this is a cleaner solution to this issue, I’m thinking of removing the AlwaysKeepTemplateDirty logic.  At the very least, I could turn it off by default in the T4MVC.settings.t4 (currently it’s on by default).  But in the end, it’s a hack and I wouldn’t mind getting rid of it altogether.

Would anyone object to this, or have concerns about this new approach?  Just let me know in the comments…

T4MVC 2.6.13: now officially in MvcContrib, with a few new features

To get the latest build of T4MVC:

Go to MvcContrib T4MVC page on CodePlex

 

A few weeks back, we announced (mostly on Twitter) that T4MVC was moving into the MvcContrib Codeplex project.  I didn’t say much about it at the time because there wasn’t a whole lot to say yet, in the sense that the move had not actually happened.  MvcContrib was transitioning from github to Mercurial, and since I wasn’t familiar with either one, I figured I’d wait for that and only have to learn Mercurial.

So to make it short, all of this has finally happen, and we can now say that T4MVC is officially part of MvcContrib!  Many thanks to Eric Hexter and Jeremy Skinner for helping make this possible.

 

So what difference does it make for it to be in MvcContrib?

The download and documentation pages moved to into MvcContrib, but that in itself is not very interesting.  The major difference is that it will now be much easier for anyone to contribute changes, given that the project is completely open source.  Previously, people were sending me suggestions by email, and the whole process was very ad hoc.

Now you can contribute changes the same way you would for the rest of MvcContrib.  Please check out this new instruction page that Jeremy just put together.  If you’ve used Mercurial before, you’ll barely need to read it.  If you have not, then you’ll need to learn the basics.  I found this site by Joel Spolsky to be a great intro.

More specifically, all the files that relate to T4MVC are under /src/T4MVC in the repository.  Note that right now, they only exist in my fork of the repository, but in a few days they’ll propagate to main.  Note that most of the files there are really just part of the test app that T4MVC is in for unit testing.  When it comes down to it, T4MVC is still just two plain text files (T4MVC.tt and T4MVC.settings.t4), and they’re under /src/T4MVC/T4MVCHostMvcApp/T4MVC Files/.  There is nothing that really needs to be built per say other than to run the tests.

 

What’s new in 2.6.13

In addition to the MvcContrib move, T4MVC 2.6.13 brings in a few new features.

Support to include an optional timestamp query string in the static file links

This idea came from Wayne Brantley.  From T4MVC.settings.t4:

// If true, links to static files include a query string containing the file's last change time. This way,
// when the static file changes, the link changes and guarantees that the client will re-request the resource.
// e.g. when true, the link looks like: "/Content/nerd.jpg?2009-09-04T12:25:48"
const bool AddTimestampToStaticLinks = false;

By default, this is turned off, so switch the flag if you want to use it.

 

Added support to recognize the [Bind(Prefix=”…”)] attribute

This one was suggested by Kris Penner.  Suppose you have an action that looks like this:

public virtual ActionResult ActionWithBindPrefixAttribute([Bind(Prefix = "newParamName")] string fieldName) {
    ...
}

What this means is that this parameter needs to get sent as ‘newParamName’ instead of ‘fieldName’ in the route values.  Previously, T4MVC was ignoring this and basically doing the wrong thing.  Now, calling MVC.MyController.ActionWithBindPrefixAttribute("Hello") will correctly generate a route value that has newParamName=”Hello”.

 

New way to easily add all the query string values to the route

Another Wayne Brantley Idea (he keeps me busy! :) ).  Suppose you want to add all the current query string values to the route values produced by T4MVC.  You can now use a new 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.

 

New MapRoute overload that supports constraints

This was just a missing overload that’s needed when you use T4MVC in your route and happen to also need some constraints.  Here is the overload:

public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, object defaults, object constraints);

 

Removed some VS2010 Beta 2 specific logic

There was some transitional logic that only made sense in beta 2, so I yanked it since everyone should be using VS2010 RC or later now.  Note that T4MVC still works fine on VS2008 SP1, and works with both MVC 1 and MVC 2.  It’s only support for the old 2010 Beta 2 that’s being removed to clean things up.

T4MVC 2.6: MVC 2 Areas support

To get the latest build of T4MVC:

Go to T4MVC page on CodePlex

 

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
    • Models
    • Views
    • Controllers
    • Areas
      • NerdDinner
        • Models
        • Views
        • Controllers
      • Blog
        • Models
        • Views
        • Controllers

So basically you still have your top level Models/Views/Controllers folders, and in addition to that you can have an arbitrary number of “Areas”, each having their own set of Models/Views/Controllers folders.

Starting with MVC 2.6, T4MVC lets you use areas in much the same way it lets you access top level items.  e.g. you can now write:

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

 

Note how we referred to the controller as MVC.NerdDinner.Dinners.

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

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

 

Lots of headaches to find a good naming pattern for Areas in T4MVC

Surprisingly, one of the things that caused the most headaches in getting this working was finding a well working naming pattern.

Here are the patterns that were on the table:

  1. MVC.NerdDinner.Dinners
  2. MVC.NerdDinnerArea.Dinners
  3. MVC.Areas.NerdDinner.Dinners

#1 is the simplest and most concise, but breaks if you have a top level controller that has the same name as the area.

#2 appends ‘Area’ to the area name to solve the conflict we caused by #1.  But while it solves the conflict, you end up with a slightly quirky naming scheme.

#3 uses an extra Areas token before the area name to avoid the conflict.  The drawback here is that you end up with one more token, meaning one more thing to type through in intellisense.

I then polled twitter to see whether people preferred #2 or #3, and most went with the cleaner and more wordy #3.

Then Nikhil suggested using a hybrid of #1 and #2, where we only append ‘Area’ to the area name if we detect a conflict with a top level controller name.  Let’s call this #4!

So I ended up supporting both #3 and #4, based on the IncludeAreasToken switch in the settings file.

But frankly, I’m considering getting rid of #3.  In the twitter poll, people didn’t like #2 mostly because it’s weird to have that extra ‘Area’ suffix.  But with the hybrid #4 solution, this funky name only occurs exceptionally, making it much less of an issue.

To end this post, I’d like to thank Nathan Roe for helping out putting together this change!

T4MVC now has a real home and a dedicated forum!

Up until now, most things related to T4MVC were happening through my various blog posts about it.  And while that was sort of working ok for a while, it was also less than ideal for a couple reasons.

First, there was no single place to go to in order to get information about it.  You’d basically have to go through the various posts that describe the various features as they were added.  And as the number of posts grew, so did the pain involved in doing that.

The second issue is that there was no good place to discuss it, ask questions and report issues.  So all of those things were mostly just happening organically as comments in my various posts, which really doesn’t work so well.  Blog Post comments don’t support threading, and things ended up being spread all over the place.

To simplify this mess, I created two things:

New T4MVC Home Page: http://aspnet.codeplex.com/wikipage?title=T4MVC

This page contains everything you may want to know about T4MVC: where to get it, the complete documentation, and links to other relevant places.  And obviously, the idea is to keep this page up to date as new features are added.

New discussion forum: http://forums.asp.net/1215.aspx

This is a standard ASP.NET forum, much like the one used for MVC and other ASP.NET topics.  If you have any questions or comments about T4MVC, this is the place to go!

Hopefully, this will help make things a little less random :)

T4MVC 2.5.01: added support for Html.RenderAction and Html.Action

To get the latest build of T4MVC:


Go to T4MVC page on CodePlex


MVC 2 Beta introduces two nice helpers called Html.RenderAction and Html.Action.  Phil Haack described them in detail on his blog, so you may want to read through that before reading this post.


Basically, they’re two additional methods that follow the standard MVC pattern of passing the controller name and action name as literal strings, and the action parameters as anonymous objects.  e.g. Copying from Phil’s example, if you have an Action like this:

public ActionResult Menu(MenuOptions options) {
return PartialView(options);
}

You can write this in your View:

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

Note how the action name is hard coded, and the MenuOptions parameter is passed as an anonymous object.  With T4MVC, you can instead write:

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

Giving you full intellisense and type checking for the controller name, action name and parameters.


Another advantage is that normally, if you use an ActionName attribute, you’ll need to pass that name instead of the action method name.  Again from Phil’s blog:

[ActionName("CoolMenu")]
public ActionResult Menu(MenuOptions options) {
return PartialView(options);
}

And you then have to pass “CoolMenu” instead of “Menu” when you make the call, which is pretty easy to get in trouble with.  But with T4MVC, the fact that you use a non-default Action Name is abstracted out, so you make the exact same Html.RenderAction(MVC.Home.Menu(…)) call.


Of course, all of this is nothing new if you’re used to using T4MVC.  It’s just the same pattern as we have everywhere else, applied to those couple new methods.  Maybe I’m just rehashing the same old benefits of strong typing and avoiding literal strings :)


On a separate note: I fixed a small bug that happened when you used a custom ActionResult without an explicit ctor.  See comments in previous post for details on the issue.

T4MVC 2.5.00 update: multiple output files and minified javascript support

To get the latest build of T4MVC:


Go to T4MVC page on CodePlex


T4MVC build 2.5.00 brings a couple of fun new features that I’ll describe in this post.  I can’t take too much credit for them as they came from users who suggested them to me, and helped out getting the code going.


Support for multiple output files


Up until now, T4MVC.tt has always generated a single file, which is the T4MVC.cs that you see get nested under it in VS.  And normally, this is the way T4 templates work: they just generate one file.  But last week I got an email from Stuart Leeks who pointed me to a new blog post from Damien Guard that describes a nice way to get around this limitation.  All of Damien’s logic is very nicely encapsulated in a class, making it pretty easy to add to an existing T4 template without having to make significant changes.


I was going to look into getting that in T4MVC, but Stuart went ahead and did it, so I didn’t have much to do myself.  Thanks to Stuart and Damien on this one!


But first I suppose we should discuss why generating multiple files is a good thing.  Quite simply, it keeps things more organized, and makes things cleaner when working with source control.  Since T4MVC can generate a fair bit of code, it is a good candidate to use this.  Specifically, T4MVC can now generate a different output file for each controller, making things more manageable.  Note that it still generated the ‘primary’ T4MVC.cs file, which contains all the top level things that don’t belong to any specific controller.


This behavior is on by default, but can be disabled in T4MVC.settings.t4:

// If true,the template output will be split into multiple files.
bool SplitIntoMultipleFiles = true;

I debated whether to make it the default, but in the end opted for it, as I didn’t see any serious drawbacks.  But I guess if you never plan to look at or deal with the generated files in any way, it doesn’t buy you a whole lot and you may choose to turn it off.


Warning if you’re using VS2010 Beta 2: Damien’s code uses the ITextTemplatingEngineHost interface, which got moved to a new Microsoft.VisualStudio.TextTemplating.Interfaces namespace in 2010 Beta 2.  So on 2010 Beta 2, you’ll need to add one extra import line to T4MVC.tt (the second line below):

<#@ import namespace=”Microsoft.VisualStudio.TextTemplating” #>
<#@ import namespace=”Microsoft.VisualStudio.TextTemplating.Interfaces” #>

Support for Minified Javascript files


This one came from Matt Wicks.  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.


The new T4MVC support makes it automatic to use one vs. the other depending on the context.  T4MVC has been supporting script files for a while, letting you write:

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

With this new feature, 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.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’.

T4MVC 2.4.04 update: MVC 2 support, new settings, cleanup, fixes

To get the latest build of T4MVC:


Go to T4MVC page on CodePlex


Though I haven’t blogged for a while about T4MVC, I’ve been making a few minor updates and only sent notification via Twitter.  Now, I have a few things that are worth discussing in a little more detail.  Note that you can see the complete list of changes from version to version in the readme.txt file that comes with it.  BTW, I used to have all this revision information directly in the .tt file, but it was getting a little long so I moved it to the readme.


The changes described below were added between version 2.4.00 and 2.4.04.


MVC 2 Preview 2 support


The most interesting things to many people is that I just made a fix to allow T4MVC to work on MVC 2 Preview 2 apps.  The reason is was not working is that the MVC team changed a number of their API’s to return MvcHtmlString instead of strings.


One challenge here is that I did not want to maintain two separate versions of T4MVC, just to account for this small difference.  Instead, I ended up adding logic in T4MVC which detects which version of ASP.NET MVC your app is using, and adapts what it generates accordingly.  The result is that the overall T4MVC experience is the same as before: you drop it into any MVC app and it should just work!


Detecting the version of MVC in use through the less-then-perfect VS DTE object model was a bit of a challenge in itself.  In case you’re curious, here is the logic I ended up with:

var vsProject = (VSLangProj.VSProject)Project.Object;

foreach (VSLangProj.Reference r in vsProject.References) {
if (r.Name.Equals(“System.Web.Mvc”, StringComparison.OrdinalIgnoreCase)) {
return r.MajorVersion;
}
}


The fact that I had to cast Project.Object to a VSProject was far from intuitive.  But anyway, I digress, this post is not about the VS DTE model :)


So, back to MVC 2, please note that this new T4MVC does not add support for any of the new features.  It simply allows it to run without blowing up, but still only supports features that existed in MVC 1.  In particular, the one big obvious feature that would be worth supporting is Areas (Phil’s blog).  This is something I’d definitely like to add support for when I get some cycles (unless someone else does it first! :) ).


Generate cleaner code that doesn’t make various tools complain


This came as various separate changes based of issues that several people mentioned.  Copying items from the revision list:



  • Put all the generated code in a T4MVC #region. This is useful to tell tools like ReSharper to ignore it.

  • Added <auto-generated /> comment to disable StyleCop in generated file

  • Added pragma to prevent compiler from complaining about missing Xml comments

  • Renamed generated classes to be CLS compliant

Generally, T4MVC is trying to be a good citizen with the various tools people are running.


New knobs in T4MVC.settings.t4


You can look at T4MVC.settings.t4 to see the list of things that are customizable.  In short, and again copying from the history list, the new knobs are:



  • Added a setting to turn off the behavior that always keeps the template dirty

  • Added a setting to set the namespace that Links get generated in

  • Added ProcessVirtualPath method to T4MVC.settings.t4 so user can write custom logic to modify client URL’s

Cleanup


I made various cleanup to the template’s logic.  In particular, I greatly simplified the logic that locates the VS project that contains the T4 template, by using the FindProjectItem() method:

// Find the .tt file’s ProjectItem
ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
Before, I was doing all kind of crazy things, mostly because I didn’t know about this method.  I’m learning the VS DTE model as I go along, as I had never used it before :)

T4MVC 2.4 updates: settings file, sub view folders, ActionName support and more

To get the latest build of T4MVC:


Go to T4MVC page on CodePlex


This post is a continuation of various previous posts on the T4MVC template for ASP.NET MVC:



I last blogged about version 2.2, and there have been a number of changes since that (you can get the full history at the top of the T4MVC.tt file).  This post describes some of those changes.


T4MVC now uses a separate settings file


Previously, if you wanted to customize T4MVC, you’d have to change T4MVC.tt directly.  This is fine until you want to grab the next build, and have to hand merge the changes.


Instead, it is now using a separate settings file called T4MVC.settings.t4.  The idea is that you can tweak some behavior by changing this file, without changing the main file.  Make sure that you copy both files when you grab 2.4 or later!


Note: it uses a .t4 extension instead of .tt, because we don’t want VS to process it directly.  Instead, it’s included by the main .tt file.  What’s nice is that the .t4 extension is also recognized by Clarius’ Visual T4.


Currently, it doesn’t support all that many settings, but it’s only a first step.  The idea is that as more customization scenarios come up, new things will appear there.  Of course, you may need some small hand merging of the settings files when you update to newer versions, but that’s a lot less painful than merging the main .tt file.


Here is what it contains in 2.4.00:

// The prefix used for things like MVC.Dinners.Name and MVC.Dinners.Delete(Model.DinnerID)
const string HelpersPrefix = “MVC”;

// 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”;

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


Support for views in sub folders


I have had a few users mention that they sometimes don’t put their views directly under Views\CtrlName, but instead put them in a subfolder.  e.g. you might have Views\Dinners\Sub\Details.aspx.


Previously, T4MVC was ignoring those.  Now, it finds them and makes them available using a matching hierarchy.


So when you have Views\Dinners\Sub\Details.aspx, you can refer to it as



MVC.Dinners.Views.Sub.Details


or if you’re within the Dinners controller, you can just write:



Views.Sub.Details


And this will evaluate to “Sub/Details”.


Support for [ActionName] attribute


Suppose you have an action method that looks like:

public virtual ActionResult Details(int id) {

By default, the action name is just the method name: “Details”.  That is, this is what normally shows up in your URLs, e.g. /Dinners/Details/2.


But sometimes, you want your action name to be different from the method name, and that’s when you would use the MVC ActionName attribute.  e.g.

[ActionName("NewActionName")]
public virtual ActionResult Details(int id) {

And now your URLs start looking like /Dinners/NewActionName/2.


Except that previously, T4MVC was ignoring this attribute, causing it to still generate the old URL!  Now, it correctly locates and honors the attribute.


The beauty is that T4MVC completely shields your code from those changes.  Without it, you’d have written:

<%= Html.ActionLink(dinner.Title, “Details”, new { id=dinner.DinnerID }) %>

And you would have had to change “Details’” to “NewActionName”.  But with T4MVC, you just write:

<%= Html.ActionLink(dinner.Title, MVC.Dinners.Details(dinner.DinnerID)) %>

And it keeps on doing the right thing no matter what the ActionName is set to!


New parameter-less overload for all actions


Previously, T4MVC was only offering pseudo-action calls that had the exact same signature as the real action method.  This usually works well, but in some POST scenarios, the parameters comes from the form and not the URL.


To solve this, T4MVC now always generates a parameter-less overloads.  e.g. suppose your action looks like:

[AcceptVerbs(HttpVerbs.Post), Authorize]
public virtual ActionResult Edit(int id, FormCollection collection) {

Where both the id and the FormCollection are things that you don’t want to pass explicitly, you can write:

<% using (Html.BeginForm(MVC.Dinners.Edit())) { %>

So you still get the benefit of not hard coding the controller and action names (what T4MVC is all about), even though you’re not using the signature that matches your real action.


Support for placing T4MVC.tt below the root of the app


Previously, T4MVC.tt had to be at the root of the MVC web application.  Some users mentioned that they preferred to have it in some other folder, like ‘Templates’.  So I made a change so it can be anywhere under the project root.


Bug fixes


There are also a number of bug fixes that are not interesting enough to discuss individually.  Please see the history at the top of T4MVC.tt for details.

Two ways to use T4 templates: support code vs. one-time generation

T4 templates have proven to be useful is a whole range of scenarios, and more and more developers are finding interesting things to do with them.

For the most part, all those scenarios fall under two very distinct categories: “support code” versus one-time generation.  Unlike my previous post on CodeDom vs. T4, here we’re not talking about making a choice between two competing technologies, but simply about using T4 in the way that makes sense for a given scenario.

Let’s start with a brief description of the two usage patterns:

Support code: here, a T4 template generates a file that you rarely need to look at, and you should never modify.  Instead, it contains “support code” that you can code against.  A great example of this is my T4MVC template.

One-time generation: here, you are executing a T4 template to generate a file that then becomes part of your project.  This file can then safely be modified.  The T4 template just gave you a starting point.  A great example of this is the Add View dialog in ASP.NET MVC.

I purposely gave two examples that relate to ASP.NET MVC to make a point that the two scenarios are not exclusive, and can play different parts within the same application.

The rest of this post will discuss various aspects of the two scenarios in more details.

 

Using T4 templates to generate “Support Code”

This is the scenario that you are most likely to have encountered when you got introduced to T4 templates.  It has a pretty low barrier of entry as it is directly supported by Visual Studio 2008.  The general flow here is:

  • You add a file with a .tt extension to your VS project
  • VS knows about those files, so it instantly sets it up to use the ‘TextTemplatingFileGenerator’ custom tool (you can see this in the .tt file’s property grid).
  • Whenever you save the .tt file, the template executes, and the generated file becomes a sub-file of the .tt file.

e.g. suppose your Hello.tt file has:

<#@ Template Language="C#v3.5" #>
<#@ Output Extension=".cs" #>

public static class Hello {
    public static void SayHello() {
<# for (int i=0; i<5; i++) { #>
        System.Console.WriteLine("Hello <#= i #>");
<# } #>
    }
}

In the solution explorer, you will see:

image

And Hello.cs will contain:

public static class Hello {
    public static void SayHello() {
        System.Console.WriteLine("Hello 0");
        System.Console.WriteLine("Hello 1");
        System.Console.WriteLine("Hello 2");
        System.Console.WriteLine("Hello 3");
        System.Console.WriteLine("Hello 4");
    }
}

Pretty simplistic stuff as far as code generation goes, but there are some key points that need to be made about it.

First and foremost, you should never modify the generated file.  You certainly can go in there and make changes, and no one will stop you.  You can even run your project and your hand modified code will execute.  But guess what will happen the next time you open Hello.tt and save it?  It will re-execute and blow away your carefully modified Hello.cs.  So resist the temptation, and don’t mess with the generated file!

That’s why I named this scenario “Support Code”.  What gets generated is code that is meant to be used by you in the rest of your app.  e.g. in the example above, you could simply call Hello.SayHello() from your console app’s Main.

Now you can take a look at the T4MVC template, which has 1000 lines and does pretty complex things to make it easier to write your MVC app.  But in essence, it works the exact same way as this trivial example: it’s a .tt file which generates a .cs file.  You rarely look at the .cs file, and you never modify it, but instead you call into the bits and pieces that it generates from your controllers and views.  It will frequently regenerate as you project changes, and that’s really the whole point: it keeps itself up to date so you don’t code against stale code.

 

Using T4 files for one-time generation

In this scenario, our purpose is to help the user create a new file for their project by pre-generating it with some useful content instead of having them start with some generic empty file.

As mentioned above, a great example of this is the ASP.NET MVC Add View functionality.  It is described in quite some detail in this post, so you may want to check it out.  I’ll use this as an illustration in this section, though of course a similar technique can be applied to other domains.  Here the general flow is:

  • You would like to generate a new View for their app
  • Instead of giving you a generic new view (which a regular item template would do), it asks you a bunch of questions:
    • What kind of view would you like, e.g. List, Details, Edit, …
    • What kind of entity is it for, e.g. Product, Customer, …
    • What master page you want to use, etc…
  • It takes all that knowledge and feeds it into a T4 template (suing a custom T4 host; a bit more on this later), which generates the view and adds it where it belongs in your project

In both the “Support Code” scenario and here, a T4 file is used to generate another file, but from here on things become totally different.  In this one-time generation scenario, the resulting file becomes a completely normal file that’s part of your project.  As such, you can freely modify it without fear of it getting overwritten, since the T4 template is no longer in the picture.  It was simply used to create the file to get you started.  It was a “one-time” generation.

One weakness of this model is that you can’t easily rerun the template.  e.g. suppose your Product class gets a new member and you want to update the view to account for it.  If you hadn’t touched to previous generated view, you can certainly delete it and rerun the Add View dialog.  But if you had made any modifications, you’re mostly out of luck, and probably better off updating your view by hand without help from the T4 template.  In spite of this weakness, the model is still very useful to get the user quickly started with their code and guiding them towards the pit of success.

Note that I didn’t say much about exactly how the T4 template gets executed in this scenario.  Unlike the “Support Code” scenario, VS is not the one doing it.  Instead, you have to do some things that are a bit more involved to make it happen.  I won’t go into details about how to write a custom T4 host here as it is beyond the scope of this post, and is well covered by other posts (in particular, check out Oleg Sych’s blog).

One last thing I’ll mention about this model is that the .tt file is normally not part of your project.  Instead, it lives somewhere else, and only its output becomes part of your project.  Well, technically, the .tt file can be in your project for easy editing, but you then have to remove the ‘TextTemplatingFileGenerator’ custom tool, because you really don’t want it to execute on its own (it would surely fail with the custom host).

 

Conclusion

To reemphasize what I said early on, these are not two scenarios that compete with each other and for which you must “pick a side”.  They are just two completely different ways of making use of T4 files.  Both are being used successfully in various projects, and they can live together happily ever after :)

AspPathGuru: A little T4 love for ASP.NET WebForms

Last month, I wrote a number of posts on using T4 templates to get strong typing in ASP.NET MVC applications. The result is the T4MVC template available on CodePlex.  This template has been pretty popular with many MVC users, and I received a huge amount of feedback on improving it.  Most of it has been integrated into the CodePlex version (see the extensive History section in the TT file!).


While T4MVC is only useful to MVC applications, someone suggested that ASP.NET WebForms applications could also benefit from some strong typing, so I put together a little T4 template that does some of that.


Unlike T4MVC which tries to cover a whole range of MVC scenarios (relating to Controllers, Actions and Views), this template just does one thing: it generates strongly typed constants that points to the path to all aspx/ascx/master files.  Since it only deals with path, I called it AspPathGuru.tt.


To use it, just drop AspPathGuru.tt (attached to this post) to the root of your Web Application.


You’ll then be able to change code that looks like:

Control uc = LoadControl(“~/UserControls/MyUserControl.ascx”);

to

Control uc = LoadControl(Paths.UserControls.MyUserControl_ascx);

The benefits are clear:



  • You get intellisense while typing the constant, helping you make sure you get it right.

  • If you ever move, rename or delete the user control, you will get a compile time error instead of a runtime error, hence catching the issue much earlier.

Some limitations you should be aware of:



  1. It only supports Web Applications, not Web Sites.  This is because T4 templates don’t get processed in Web Sites (at least I couldn’t get them to, maybe there is a way).

  2. You need to save the .tt file for the generation to occur.  So whenever you add or rename aspx/ascx file that you’d like to point to using the generated constant, you should save it.  In T4MVC, I implemented a workaround that causes the template to run whenever you build, and we could do this for AspPathGuru as well, but I wanted to keep it nice and simple to stat with.

  3. It’s probably obvious, but the generated constants are only usable is places where you can write code, like the LoadControl call above.  So don’t try to use it as the ‘src’ in a <%@ Register %> directive; it will not work!

Anyway, at this point this is just a simple template that covers this one scenario, and is certainly much less ambitious than T4MVC.  Generally, I think that there just aren’t as many areas in WebForms that can benefit from code generation compared to MVC, but at least this is one!


Let me know if you find this useful, or if you can think of other areas where a T4 template could benefit WebForms applications.