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 🙂

Comments (8)

  1. @Dave, I got the new bits and everything looks good.  Thanks!  Might I suggest putting this up on the MVC 2 download page as well?

    http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=33836

    Regards,

    –Ken

  2. David Ebbo says:

    @Ken, thanks for the suggestion. I have added it to the MVC 2 page as well. Now I just need to remember to update both when I make changes! 🙂

  3. Gabriel says:

    I figured out why I’m getting this error: error CS0246: The type or namespace name ‘T4MVC_ActionResult’ could not be found (are you missing a using directive or an assembly reference?)

    It’s due to having a method that returns a ViewResult instead of an ActionResult. If I change ViewResult to ActionResult it works. Any ideas on how to proceed?

  4. David Ebbo says:

    @Gabriel: it should generally work fine with ViewResult. e.g. I just tried creating an new MVC app and changing one of the actions to return ViewResult, and it worked fine.  Maybe there is something else about your app that’s different?  If we can isolate the difference, I’m sure we can find a way to address it.

  5. Gabriel says:

    Here are steps to reproduce the error:

    1) In VS2008, create a new ASP.NET MVC Web Application (1.0) (.NET Framework 3.5).

    2) Say No to "Create a test project".

    3) Add T4MVC.tt and T4MVC.settings.t4 to the project.

    4) Remove AccountController and its corresponding Account Views.

    5) Remove HomeController.About() and its corresponding Home/About View.

    6) Change HomeController.ActionResult Index() to ActionResult Index(string param1).

    7) Check that build succeeds.

    8) Change HomeController.ActionResult Index() to ViewResult Index(string param1).

    9) Build fails.

  6. David Ebbo says:

    @Gabriel: thanks for the detailed step! I can repro the issue now.

    Try the following fix around line 85.  Change:

           public ActionResult <#= method.Name #>() {

               return new T4MVC_ActionResult(Name, Actions.<#= method.ActionName #>);

           }

    to:

           public <#= method.ReturnTypeFullName #> <#= method.Name #>() {

               return new T4MVC_<#= method.ReturnType #>(Name, Actions.<#= method.ActionName #>);

           }

  7. Harry M says:

    LOVING the template. Any chance you could make it know parameter names on actions so that I can map my input field names.

    e.g.

    <% using (Html.BeginForm(MVC.Gallery.SaveNewGallery())) { %>

    <%= Html.TextBox("name") %>

    <input type="submit" />

    <%} %>

    becomes

    <% using (Html.BeginForm(MVC.Gallery.SaveNewGallery())) { %>

    <%= Html.TextBox(MVC.Gallery.Actions.SaveNewGallery.Params.Name) %>

    <input type="submit" />

    <%} %>

    also it would be cool if the t4 template would check itself for new versions! Keep up the work, its amazing

  8. David Ebbo says:

    @Harry M: the param names is an interesting idea that someone else recently suggested.  I just have to find the time to get to it! 🙂  Copying from the reply I had sent to that suggestion:

    That’s an interesting idea, and it shouldn’t be too hard to do.  I think we may even be able to support:

    MVC.ScheduledActivity.Actions.ForPerformancePlanRange.sportProgramId

    i.e. without the extra ‘Params’ token.  This could be done by making the action (i.e. Actions.ForPerformancePlanRange) an object with a String operator instead of just being a string.  This way it could be used as a string, or as a way to access parameters.

    If you feel like giving it a try, everything should be around the place where the template has:

           public class ActionNames {

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

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

    <#          } #>

           }

    I’ll put it on my TODO list as well, but may not be able to get to it soon.