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[] {

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


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


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.

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.

Comments (36)

  1. Jonatan Berggren says:


    I’m using your excellent template but are having som difficulties. I use S#arp architecture where the controllers sits in separate assembly away from the main webbsite.

    I copied and modified your script so that it works for me but it will be a pain to hand merge those changes to new versions.

    Please make i configurable which project the controllers are in instead of assuming that it is the same as the views.

    Thanks for a great tool!

  2. cowgaR says:

    will this excellent project be included in ASP.NET MVC v2?


  3. sjnaughton says:

    This is real cool it just gets better and better.

    These things can start to grow and grow and soon have a life of their own.

    Steve 😀

  4. David Ebbo says:

    Jonathan: could you contact me by email so we can discuss exactly how things work in this architecture?  e.g. the controllers are in a different assembly, but the views stay in the standard structure?



  5. David Ebbo says:

    cowgaR: that’s a good question, which hasn’t really been discussed. I need to chat with the MVC team guys about it. One concern is that including it would then make it harder to rev. Though I suppose it could include some stable version of it, with the option to go replace it by a newer one from CodePlex.

  6. Esbe says:

    I am also using S#arp architecture and have attempted to adapt the script for Controllers living in a separate assembly, but with no success.

    Can you please include support for multiple projects? Jonatan Berggren, any patch to suggest?

  7. Esbe says:

    S#arp architecture is here: http://sharparchitecture.net/

    And yes, it puts the Controllers in their own project.

  8. Daniel says:

    I’m loving this t4 and considering it in a project, but am running into an issue with source control.   I’m using Vault 3.5 with the checkin/checkout option (ie not edit-merge-commit).  Every time I check in T4MVC.tt, it immediately checks itself back out, presumably because it’s re-generating the code.  

    I tracked the problem to line 435: projectItem.Document.Saved = false;  Commenting this out means I have to manually run the template instead of doing so every build, but I’m cool with that.  I guess this has to do with the SCC style and probably isn’t an issue for SVN people.  Maybe this should be an option in the settings though?

    It does seem to play fine with source control when changing up the controllers and actions.

  9. David Ebbo says:

    Daniel: Good point! I just dropped a new 2.4.01 build which adds this support through a new AlwaysKeepTemplateDirty flag in T4MVC.settings.t4

  10. Daniel says:

    Thanks! I just found the Links.Content properties don’t work so well for CSS, because of the way ASP.NET processes the head runat=server and link tags. I Binged and found someone else with the same issue: http://stackoverflow.com/questions/987535/asp-net-webform-css-link-getting-mangled

    My solution was to simply do an HtmlHelper to output the entire link tag:

           public static string CssLink(this HtmlHelper helper, string cssPath)


               return string.Format("<link href="{0}" rel="stylesheet" type="text/css" />", cssPath);


    Not sure the best way to approach in T4MVC, or even if you’d want to, but one thought would be to detect the extension and enable:


  11. David Ebbo says:

    Daniel, this issue has come up before. Have you tried simply removing the runat=server on the head tag?  Chances are it doesn’t do anyting for you.

  12. Daniel says:

    @David I did try just removing the runat=server, and that would have worked, but in my case I was setting Page.Title on the viewpage, which apparently requires the runat=server.  I got an error to this effect when I removed runat=server.

    May be out of the scope of what you want to do with T4MVC, though.  Anyway, keep up the great work!

  13. David Ebbo says:

    Daniel: Darn. I wish the <head> tag didn’t try to be so smart. Maybe your Css.Content.site_css idea is the way to go. I’ll put that on the list!

  14. Steve says:

    It’s great the the T4 templates support the ActionName attribute now.  However, I’d like to use the generated template code *IN* the ActionName attribute.  So, the example you gave was this:


    public virtual ActionResult Details(int id) {

    but I want to be able to do this:


    public virtual ActionResult Details(int id) {

    But I can’t do this because the action variables were defined as readonly instead of constants.  Any reason for this?

  15. David Ebbo says:

    @Steve: I’m a bit confused by what you’re trying to do. How can you use the NewActionName constant in the ActionName attribute since this attribute is what drive the constant?  Isn’t that a chicken and egg problem?

    The reason they are not C# constant is that MVC.MyController is an object instance, and so is MVC.MyController.Actions.  Constants are not accessible through instances, only through types.

  16. Steve says:

    @David – Sorry, best to illustrate with a more real-world example rather than names like "NewActionName".  Let’s say I already have a controller method calls Create() which simply displays my form:

    public ActionResult Create()

    I have a second C# controller method called Save() but I want to apply the ActionName attribute to it so it *looks* like it gets posted to create like this:


    public ActionResult Save(int id)

    that’s the typical use for the ActionName attribute.  So I’m wondering if there is a way to avoid the hard coded "Create" string in the ActionName attribute.  by doing something like this:


    public ActionResult Save(int id)

    but you may have just given the answer.  I didn’t realize the generated code was attached to instances – so it looks like what I’m suggesting is not possible with the current implementation.  It would have to instead generate a bunch of static classes with the constants strings and that may not be worth it or desirable.

  17. David Ebbo says:

    @Steve: ok I understand your scneraio now and it makes sense. Indeed, I can’t think of a simple workaround with the current implementation. Note that there are good reasons for those to be instances, in term of being able to write MVC.MyController.Create(). But when it comes to the simple ‘Actions’ and ‘Views’ constant, I can see how working with instances is not a great as it could be.

    Maybe there is some smart way to keep the best of both world.

  18. Steve says:

    @David – Hmm, I’m trying to think through the implications if *only* the "Name" member was constant.  That would allow:


    but then you would not longer be able to do:


    Right now it looks like the primary consumer of the Name member, is code like this:

    return new T4MVC_ActionResult(Name, Actions.Save);

    that would have to be changed to this:

    return new T4MVC_ActionResult(MyController.Name, Actions.Save);

    Agreed that the controllers themselves definitely have to remain instances as you mentioned. Not sure if this would have other adverse affects elsewhere on the API.

  19. David Ebbo says:

    @Steve I think the Name could indeed be changed to be a const, but would that really help your scenario? That would let you name your Action after your Controller, but that seems like an unusual thing to do 🙂

  20. Steve says:

    @David – LOL – I’m an idiot – I didn’t mean to put the controller’s name in the attribute – obviously that does not make sense. 🙂

    What I *meant* to say, was to wonder if there was any way to make the Action’s names constants. So that it could be written like this:


    similar to my original example but not prefaced with the MVC static class which holds instances.  But even so, there is a chicken and the egg problem with the code generation so this simply might not be practical.

  21. David Ebbo says:

    @Steve Sorry, didn’t mean to imply that at all; I figured I was misunderstanding you! 🙂

    But actually, what you describe can work.  e.g. in T4MVC.tt, try adding this block after the block that has ‘public class _Actions’:

           public class TestActions {

    <#          foreach (var method in controller.ActionMethodsWithUniqueNames) { #>

               public const string <#= method.ActionName #> = <#= method.ActionNameValueExpression #>;

    <#          } #>


    Now you can write what you want, e.g. [ActionName(MyController.TestActions.Index)], and I don’t think you’ll hit a chicken/egg problem.

    But the reason I’m hesitant to completely replace the existing mechanism with this is that MVC Views typically don’t import the Controller’s namespace, so you wouldn’t be able to easily use them in there.

    But it’s conceivable to have both if we can do it without causing confusion.

    Maybe we should continue this conversation in email, it’s getting long! 🙂

  22. Dave R. says:

    I’m sorry if this has been covered in a separate discussion, but shouldn’t the default HelpersPrefix be ‘Mvc’, not ‘MVC’ – the .NET convention is for acronyms longer than two characters to only have their first letter capitalised. (Yes, I’m probably being pedantic!)

    Thanks for the template. It’s a great example for those of us getting started with T4.

  23. David Ebbo says:

    @Dave R: you’re probably right. I had some concerns that this would cause naming conflicts with the Mvc namespace (as in System.Mvc), which is why I went upper case.  Note that you can easily change it by setting HelpersPrefix in T4MVC.settings.t4.

  24. austegard says:

    Am I really the only one here to run StyleCop on my MVC project?

    The T4MVC.cs class generates all sorts of StyleCop warnings.  To get around them I first tried to add a project file Compile entry:

    <Compile Include="T4MVC.cs">






    Unfortunately that only works OUTSIDE Visual Studio.

    I then tried some of the solutions outlined at http://sergeyshishkin.spaces.live.com/blog/cns!9F19E53BA9C1D63F!253.entry

    The region works, but the easiest is to simply put a

    // <auto-generated />

    comment at the top of the generated file.

    Would be nice to see this included in the next release.

  25. Anthony says:

    Just a thought…

    In the next version of template do you recon you could add in a variable into the config which allows you to specific a URL prefix for content assets.

    The reason being is that we place everything that is in Assets in under a separate domain when we deploy. The folder structure and everything is the same, its just the domain which is different. By doing this we can Split Components Across Domains which enables better parallel downloading of assets and have a Use Cookie-free Domains for our assets.

    It would also be nice if for each of the assets, in addition to the property which renders out assets path, there was a method that could be called by which you could pass in a domain name (note if done correctly this may remove the need for the config above). The reason being we have multiple static domains which serve up our assets.

    The important things to make clear is that all our Assets are managed within the MVC project (hence the template finds them just fine), its a deployment task where we take a copy of the assets folder and place it under different domains.

    Let me know if this makes sense.

  26. Anthony says:

    Also due to the client side caching strategy we use it would be great if we could define a postfix. This would allow us to do something like "http://static.stuff.com/Assets/Scripts/test.js?v1.1&quot;… that way we can force browser to re-get the content.

  27. austegard says:

    The link to sergey’s blog is broken above – here is a shortcut: http://bit.ly/stylecopignore

  28. David Ebbo says:

    @austegard: I just uploaded build 2.4.02 to CodePlex, and added the auto-generated comment as you suggested.

  29. David Ebbo says:

    @Anthony: I just uploaded build 2.4.02 to CodePlex.  It has a new LinksNamespace setting that you can change in T4MVC.settings.t4.

    I *think* this is what you meant but may be wrong.  If needed, we can take this offline by email to discuss further.

  30. David Ebbo says:

    @Anthony: reading your comment again, it’s clear that this is not what you meant! 🙂  Let’s take this offline and discuss.

  31. I am using T4MVC 2.4.03 with the MVC 2.0 preview 2 and with one small change to the template its working great.

    The System.Web.Mvc.Html.LinkExtensions.RouteLink extension method now returns type MvcHtmlString rather than string, as the template expects.  So all calls of the form:

    return htmlHelper.RoutLink(…);

    need to be update to read:

    return htmlHelper.RoutLink(…).ToHtmlString();

    and voila, it works!  Hope this helps.

  32. David Ebbo says:

    @Ken: indeed, this was broken. I just updated a fixed build (2.4.04) and blogged it. Thanks!

  33. Gabriel says:

    Thanks for all your work! I tried dropping it into a simple app I’m working on (based on Steve Sanderson’s book) and I got the following error on build:

    error CS0246: The type or namespace name ‘T4MVC_ActionResult’ could not be found (are you missing a using directive or an assembly reference?)

    It seems the template is not generating this class. But when I try dropping it a newly-created app, it works. Any ideas?

  34. David Ebbo says:

    @Gabriel, not sure what would cause that. If you email me a zipped minimal repro, I’ll be glad to take a look.

  35. Divi says:

    Hi, I converted my solution to run with VS2010 from VS2008. But I'm still running .Net 3.5 instead of 4. T4MVC has stopped working and is not able to generate any code. Would be great if you could please help with this. Thanks.

  36. David Ebbo says:

    @Divi: could you please post a question on StackOverflow? It may take a few back and forth to figure it out, and blog comments quickly get messy!

Skip to main content