Welcome to MSDN Blogs Sign in | Join | Help

Peter Blum’s new blog and his cool new data source controls

Peter Blum has been well known is the ASP.NET world for many years for writing a whole suite of powerful controls, which you can read all about on his site.  One thing that was missing on Peter’s resume is that he never had a blog.  Well he started one earlier this month, and is making up for the lost time in a big way, with already 11 posts!  And we’re not talking about small posts that just point to other people’s stuff (unlike this post I suppose!), but real with useful meaty content.  Make sure you check out his blog at http://weblogs.asp.net/peterblum/.  I hope he keeps the good stuff coming!

In particular, Peter has been working hard on some interesting data source controls that work with Visual Studio 2010.  He’s calling them the ‘Versatile DataSources’, and is making it all available for free on CodePlex.

The following posts on his blog describe the data source controls:

The simplest way to try out his controls is to download them from CodePlex.  The package contains a rich set of samples that you can directly run and play with.  You’ll need VS2010 Beta 2 to run this, so if you don’t already have it, get it from here.

I haven’t fully tried everything yet, but the one I played with the most is his POCODataSource, which is quite interesting.  The core idea is very simple: you give it a type and it makes it easy to put up a WebForms UI to fill up an instance of that type.  The UI supports full validation using standard model annotations supported by Dynamic Data.

The beauty is that it’s really quite easy to use.  The data source declaration looks something like this (borrowed from Peter’s samples):

   <poco:POCODataSource ID="POCODataSource1" runat="server" POCOTypeName="CEOEmailGenerator" />

 

For the actual UI, you can use any standard ASP.NET data control like DetailsView, FormView or some similar 3rd party control.  Then when an Update operation happens, you simply access the built instance from the data source using  POCODataSource1.POCOInstance.  At that point, you can do whatever you want with it.  In Peter’s sample, he ends up calling an action method directly on the object, e.g.

protected void FormView1_ItemUpdated(object sender, FormViewUpdatedEventArgs e) {
   if (Page.IsValid) {
      ((CEOEmailGenerator)POCODataSource1.POCOInstance).Send();
   }
}

But I don’t see anything that ties you to this pattern, and you could instead just call some helper method and pass the object if your object doesn’t have an action method itself.

Anyway, check it out in much more details on Peter’s blog!

Using C# Dynamic to simplify ADO.NET Data Access

Update (11/7/09): fixed Execute() method per Richard’s suggestion to wrap IDataRecord instead of Reader.

Recently, I started playing around with C# dynamic, and blogged how it could be used to call static class members late bound.  Today, I was talking to Phil Haack, who I think had talked to ScottGu, and he mentioned that it would be cool to use dynamic to simplify data access when you work directly with SQL query.  So I thought I’d play around with that, and it didn’t take much code to make it work nicely.

So the scenario is that you’re not using any fancy O/R mapper like LINQ to SQL or Entity Framework, but you’re directly using ADO.NET to execute raw SQL commands.  It’s not something that I would personally do, but there are a lot of folks who prefer this over the higher level data access layers.

So let’s look at an example of what we’re trying to improve.  Let’s borrow an MSDN sample about SqlCommand:

string commandText = "SELECT OrderID, CustomerID FROM dbo.Orders;";
using (var connection = new SqlConnection(Settings.Default.NorthwindConnectionString)) {
    using (var command = new SqlCommand(commandText, connection)) {
        connection.Open();
        using (SqlDataReader reader = command.ExecuteReader()) {
            while (reader.Read()) {
                Console.WriteLine(String.Format("{0}, {1}", reader[0], reader[1]));
            }
        }
    }
}

And now let’s assume that we’re only ever interested in making one select query at a time, which lets us abstract out some of the details about the SQL Connection.  By writing some nice little helpers that make use of dynamic, we’re able to write something much simpler:

string commandText = "SELECT OrderID, CustomerID FROM dbo.Orders;";
foreach (var row in SimpleQuery.Execute(Settings.Default.NorthwindConnectionString, commandText)) {
    Console.WriteLine(String.Format("{0}, {1}", row.OrderID, row.CustomerID));
}

A few things to note:

  • We pretty much just make one method call, and directly get back objects that we can work with.  Contrast this with having to deal with SqlConnection, SqlCommand and SqlDataReader.
  • We use a standard enumeration pattern, while SqlDataReader makes you call reader.Read() on every iteration, which looks ugly.
  • And the big one: we get to access the properties directly on the row object, thanks to dynamic!  e.g. we can write row.OrderID instead of reader[0] (or reader[“OrderID”])

So how does it all work?  First, let’s take a look at the SimpleQuery.Execute helper method:

public static IEnumerable<dynamic> Execute(string connString, string commandText) {
    using (var connection = new SqlConnection(connString)) {
        using (var command = new SqlCommand(commandText, connection)) {
            connection.Open();
            using (SqlDataReader reader = command.ExecuteReader()) {
                foreach (IDataRecord record in reader) {
                    yield return new DataRecordDynamicWrapper(record);
                }
            }
        }
    }
}

So it’s basically the same as the MSDN code, except that it wraps the reader that it returns in a DataRecordDynamicWrapper, which is what makes the dynamic magic work.  Also, note that the method returns IEnumerable<dynamic>, which is why we’re able to just use ‘var row’ in the test code (which I think looks nicer than ‘dynamic row’).

So now all that’s left to look at is DataRecordDynamicWrapper, which is incredibly simple:

public class DataRecordDynamicWrapper : DynamicObject {
    private IDataRecord _dataRecord;
    public DataRecordDynamicWrapper(IDataRecord dataRecord) { _dataRecord = dataRecord; }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        result = _dataRecord[binder.Name];
        return result != null;
    }
}

All it does is index into the data record to get the value for a given property name.

I think what I did with static methods in my last post was probably a bit of an abuse of dynamic, because we were dealing with statically types objects, and there are alternatives that would have avoided the need for dynamic.  But here, it’s I think a more legitimate use, because we’re dealing with data record objects that are intrinsically untyped.  While dynamic of course doesn’t give us strong typing, it at least makes it more pleasant to deal with.

One last thing worth noting is that to make this real, we should add support for SQL parameters, which makes it easier to write SQL code that is not vulnerable to SQL-injection attacks.  That could easily be done by passing additional params to SimpleQuery.Execute.  This sample is more of a proof of concept and an excuse to mess around with dynamic :)

Zipped sample is attached to this post.

Posted by davidebb | 16 Comments
Filed under: ,

Attachment(s): DataReaderWithDynamic.zip

Using C# dynamic to call static members

By now, you’ve probably heard that C# 4.0 is adding support for the dynamic keyword, which introduces some aspects of dynamic languages to C#.  I had not had a chance to really try it, but recently I was reading Bertrand Le Roy’s post on the topic, and was sort of looking for a good opportunity to use it.

Today, I found a scenario which I thought it would work great for, but it turned out not to be supported out of the box!

The scenario is to call static class members using dynamic.  That probably sounds crazy, so let’s look at an example.  Say you have these two classes:

public class Foo1 {
    public static string TransformString(string s) { return s.ToLower(); }
    public static string MyConstant { get { return "Constant from Foo1"; } }
}

public class Foo2 {
    public static string TransformString(string s) { return s.ToUpper(); }
    public static string MyConstant { get { return "Constant from Foo2"; } }
}

Note that they are unrelated classes, but share some members with the same signature.  In a sense, you could say that they two classes share a duct tape signature.

Now here is the problem we’re trying to solve: given a System.Type object of either class (or any other random class that shares those members), how can you call those members?  Concretely, we’re trying to implement this method:

static void MakeTestCalls(Type type) {
    // Call TransformString("Hello World") on this type

    // Get the MyConstant property on this type
}

How can we implement this method? Ok, we can do it the old fashion way using reflection, e.g.

static void MakeTestCalls(Type type) {
    Console.WriteLine(type.GetMethod("TransformString").Invoke(null, new object[] { "Hello World" }));
    Console.WriteLine(type.GetProperty("MyConstant").GetValue(null, null));
}

That works, but it’s ugly.  These are the very type of things that dynamic is supposed to improve.  So my first naive attempt was to do this:

static void MakeTestCalls(Type type) {
    dynamic fooTypeDynamic = type;

    Console.WriteLine(fooTypeDynamic.TransformString("Hello World"));
    Console.WriteLine(fooTypeDynamic.MyConstant);
}

Basically, the theory was that when assigning a System.Type to a dynamic variable, it would let you call static members from it.  I didn’t really expect it to work, but I at least had to try! :)  And sure enough, it didn’t work, blowing up with: “Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.RuntimeType' does not contain a definition for 'TransformString'”.

So I then posted the question on our internal C# list, and got a reply from C# guru Eric Lippert, basically saying that it was a potentially interesting idea but was just not supported in C# 4.0.  Fair enough, this is only the beginning of C# dynamic, and it doesn’t do everything.

But I then went back to Bertrand’s post where he gives a great sample of how you can teach dymamic new tricks by implementing a custom DynamicObject.  And it turned out to be relative simple.  Here is the class I ended up with:

public class StaticMembersDynamicWrapper : DynamicObject {
    private Type _type;
    public StaticMembersDynamicWrapper(Type type) { _type = type; }

    // Handle static properties
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        PropertyInfo prop = _type.GetProperty(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);
        if (prop == null) {
            result = null;
            return false;
        }

        result = prop.GetValue(null, null);
        return true;
    }

    // Handle static methods
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        MethodInfo method = _type.GetMethod(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);
        if (method == null) {
            result = null;
            return false;
        }

        result = method.Invoke(null, args);
        return true;
    }
}

The idea is pretty simple: when the runtime needs to call something, it basically asks you to do it.  It passes you the method (or property) name and the parameters, and you take it from there. And in this case, of course, we’re using reflection.

Once we have that, the real fun starts as we’re now able to call our static members using dynamic!

static void MakeTestCalls(Type type) {
    dynamic typeDynamic = new StaticMembersDynamicWrapper(type);

    // Call TransformString("Hello World") on this type
    Console.WriteLine(typeDynamic.TransformString("Hello World"));

    // Get the MyConstant property on this type
    Console.WriteLine(typeDynamic.MyConstant);
}

Note how we wrap the type into our custom DynamicObject is order to have the dynamic invocations go through us.

Now you might say that the scenario above where you have two classes with identical static members doesn’t seem like something that would actually occur commonly in real apps.  But once you start bringing in generics, it can actually be more common.  e.g. suppose you have something like:

public class Table<T> {
    public static IEnumerable<T> Records { get { [return records from database table T] } }
}

The idea is that the class is an abstraction for a database table.  So Table<Product>.Records returns an IEnumerable<Products>, and Table<Category>.Records returns an IEnumerable<Category>.  Now suppose you’re writing some table agnostic code that can work with the data from any table.  You have a System.Type for some Table<T>, and you need to get its Records property.  Even though it seems like it’s the same Records property coming for a base class, the reality is that it’s a completely different property for each T, and C# provides no simple way of making the call.  But with the technique above, you get to access the property with a much simpler syntax than with reflection.

The zipped sample is attached to this post.

Posted by davidebb | 12 Comments
Filed under: , ,

Attachment(s): DynamicCallsToStaticMembers.zip

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

To get the latest build of T4MVC:

Go to download 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 :)
Posted by davidebb | 8 Comments
Filed under: , , ,

C# method overload resolution can play tricks on you

While working on T4MVC, I ran into a very tricky C# compiler behavior that I thought I’d share.  T4MVC creates various overloads that allow you to avoid literal strings.  One such case is that you can replace:

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

with

routes.MapRoute(
    "UpcomingDinners",
    "Dinners/Page/{page}",
    MVC.Dinners.Index()
);
This works because T4MVC adds its own MapRoute overload extension method (which it puts in the global namespace):
public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result);

And of course I had tested this out before publishing the template, and it worked perfectly well with NerdDinner, which I’ve been using as my little test app.

But then, a developer sends me an email telling me that while the template is generally working well for him, this one scenario is not.  Instead of it calling the T4MVC overload, it binds to the (less specific) overload in ASP.NET MVC (coming from the System.Web.Mvc namespace):

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

This was very puzzling, since the call was passing an ActionResult, and the more specific overload should ‘win’.  Eventually, we discovered that the only difference between my working app and his non-working app is that he was putting his ‘using’ statements inside the namespace, while I was putting them outside!

e.g. I had:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace NerdDinner {

    public class MvcApplication : System.Web.HttpApplication {

While he had:

namespace NerdDinner {
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;

    public class MvcApplication : System.Web.HttpApplication {

Note that both ways are perfectly valid, and if you look at C# samples out there, you’ll find a mix of both.  It pretty much comes down to personal preference, and you would generally expect them to behave the same way.  So why the heck was that causing the C# binding behavior to change?!

It turned out that the root of the issue was that T4MVC was generating its overload in the global namespace (i.e. not in a namespace).  So it had to do with the way C# searched namespaces (see C# spec for details), causing it to find the System.Web.Mvc overload before it even considers the global namespace when the using statements are inside.  While when they’re outside, System.Web.Mvc and the global namespace are looked at together, letting the more specific overload take over.  Tricky stuff!

So what’s the moral from this story?  There are a couple:

Putting things in the global namespace is evil.  I was guilty of doing it in T4MVC.  I guess I had figured that being generated code, we just wanted it to be available everywhere without forcing the user to deal with namespaces, but it was a mistake.  I have since moved away from that, instead adding things to the System.Web.Mvc namespace.  This may be slightly questionable as well, since it’s adding to a framework namespace it doesn’t own, but given the nature of them T4 template, it’s acceptable.  Now it works equally well wherever the ‘usings’ are.

Overloads that take ‘object’ are pretty evil.  ASP.NET MVC makes common use of the anonymous object pattern to pass default values.  This is why their overload is typed as object, and lets you pass new { controller = "Dinners", action = "Index" }.  But while this is convenient, it really increases the odds of confusing the resolution rules, so I’m not a huge fan of them.  Some people will go as far as saying the method overloads are generally evil, but I think they are very useful when not abused.

Posted by davidebb | 5 Comments
Filed under: ,

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

To get the latest build of T4MVC:

Go to download 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.

Posted by davidebb | 34 Comments
Filed under: , , ,

Using an Associated Metadata Class outside Dynamic Data

A while back, I blogged about how ASP.NET Dynamic Data apps can uses an Associated Metadata class (aka a ‘buddy’ class) to add metadata attributed to properties defined in a generated class.  It’s a mostly ugly thing that was made necessary by limitations of the C# and VB.NET languages: they don’t let you add attributes to properties defined in another partial class.

What I didn’t mention there is that this ‘buddy’ class mechanism is actually not specific to Dynamic Data apps, and can in fact be used anywhere.  Since I’ve recently heard of several cases of users trying to do something similar, I’ll describe how it’s done.  If you’re familiar with TypeDescriptionProviders (which have been around since ancient times), this will look very trivial.

I will illustrate this in a very simple console app to keep all other distractions out of the picture (the full sample is attached to the post).  So the general scenario is that we have a generated class somewhere, e.g.

// Assume that this is generated code that should never be hand modified.
// Hence metadata attributes can't be added directly here

public partial class Product {
    public string Name { get; set; }

    public int UnitsInStock { get; set; }
}

Instead, the buddy provider let’s you write:

[MetadataType(typeof(Product_Metadata))]
public partial class Product {
}

class Product_Metadata {
    [DisplayName("The Units In Stock")]
    public object UnitsInStock { get; set; }
}

This works in Dynamic Data, but if we’re in some other context, no one will find our Product_Metadata ‘buddy’.  In our to hook it up ourselves, we just need to make one call!

TypeDescriptor.AddProvider(
    new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Product)),
    typeof(Product));

Pretty trivial stuff: we instantiate a TypeDescriptionProvider (with a somewhat scary name, I’ll give you that) and we register it by calling TypeDescriptor.AddProvider.

Once we do that, the attributes on the ‘buddy’ class magically show up as if they were defined on the real class:

// Get the property descriptor for UnitsInStock
PropertyDescriptor propDesc = TypeDescriptor.GetProperties(
    typeof(Product)).Find("UnitsInStock", true);

// Get the display name attribute, which is not actually on the property,
// but on its counterpart in the 'buddy' class
var displayName = propDesc.Attributes.OfType<DisplayNameAttribute>().First();

And that’s pretty much it.  One important thing to notice here is that we are not using the standard reflection API (which would look like typeof(Product).GetCustomAttributes()), but we are instead using the TypeDescriptor API from the System.ComponentModel model namespace.

So to summarize, you can easily use this ‘buddy’ class mechanism anytime you deal with generated code that you need to annotate with attributes.  And even though it’s not pretty to have to use that extra class, it’s comes very handy when there is no alternative.  Maybe the day will come when C# will support doing this more cleanly, but in the meantime that’ll have to do!

Posted by davidebb | 8 Comments
Filed under: ,

Attachment(s): SimpleBuddyTypeSample.zip

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 :)

Posted by davidebb | 6 Comments
Filed under: , ,

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.

Posted by davidebb | 7 Comments
Filed under: ,

Attachment(s): AspPathGuru.zip

T4MVC 2.2 update: Routing, Forms, DI container, fixes

To get the latest build of T4MVC:

Go to download page

This post is a continuation of various recent posts, most notably:

First, I’d like to thank all those who are using the MVC T4 template and sent me suggestions and bug reports.  Most issues have been addressed, and most suggestions have been integrated.  I’m up to the 8th CodePlex drop, and it’s only been a week! You can see the history of changes at the top of the .tt file.

Frankly, when I started playing with this, I just thought it’d be a fun thing to spend the afternoon on.  Instead, I have probably spent close to half my time working on it in the last week.  And I do have other things to work on! :)  But it’s been a lot of fun, so no regrets!

I’m not going to detail every change since the last blog post, since many are bug fixes that are not particularly exciting (though they help make the thing work!).  Instead, I’ll just focus on a few new key areas.

Strongly typed support for MapRoute

This is similar to the RedirectToAction/ActionLink support, but applied to route creation.  The original Nerd Dinner routes look like this:

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

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "" }
);

The ‘defaults’ line of each route is what we’re trying to fix.  It makes heavy use of anonymous objects and literal strings, which should be avoided whenever possible.  If your controller or action name change, you won’t easily catch it.

Previous version of T4MVC let you change that line to:

new { controller=MVC.Dinners.Name, action=MVC.Dinners.Actions.Index }

This fixes the issue for the most part, but it’s a bit wordy and doesn’t support refactoring.  Now, the latest version of T4MVC lets you change those routes to:

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

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    MVC.Home.Index(),
    new { id = "" }
);

Note how we are now pointing at the action by simply making a call to it (though it’s a pseudo-call, and your action method won’t actually be called).

Also note that in the second route, we’re still using an anonymous object for the ‘id’.  This is because even though the route expects an id, the default Action method (HomeController.Index()) doesn’t take one.  You could get around this by adding a ‘string id’ param to HomeController.Index() and ignoring it in the method.  You could then write MVC.Home.Index(null) in the route and avoid all anonymous objects.

BeginForm support

This came courtesy of Michael Hart.  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.

Support for Dependency Injection containers

Though this is a bug fix rather than a new feature, I’ll mention it because it affected several people and had me puzzled for a while.

The root of the problem is that T4MVC generate new constructors for the controllers, and that DI containers use a somewhat arbitrary algorithm to decide what constructor to use.  And in those users’ cases, they ended up calling the new constructor generated by T4MVC, messing things up badly.

The fix was simply to change the generated constructor to be protected, to prevent DI containers from using it.  Since T4MVC only uses it when it instantiates the controller derived class, protected works fine.

Well, I should note that I haven’t actually verified that the fix addresses the issue, but I’m sure those users will tell me if it doesn’t! :)

Posted by davidebb | 17 Comments
Filed under: , , ,

The MVC T4 template is now up on CodePlex, and it does change your code a bit

Short version: the MVC T4 template (now named T4MVC) is now available on CodePlex, as one of the downloads in the ASP.NET MVC v1.0 Source page.

Go to download page

Poll verdict: it’s ok for T4MVC to make small changes

Yesterday, I posted asking how people felt about having the template modify their code in small ways.  Thanks to all those who commented!  The fact that Scott Hanselman blogged it certainly helped get traffic there :)

The majority of people thought that it was fine as long as

  • It’s just those small changes: make classes partial and action methods virtual. Don’t mess with ‘real’ code!
  • It asks for permission, or at least tells you what it’s doing

I started looking for a way to pop up a Yes/No dialog, but ended up going with a slightly different approach: T4MVC adds a warning line for every item it modifies.  e.g. when you run it, you might see these in the warnings area:

Running transformation: T4MVC.tt changed the class DinnersController to be partial
Running transformation: T4MVC.tt changed the action method DinnersController.Index to be virtual

Some people were worried about version control.  I tried using TFS, and everything worked fine.  i.e. when the template modifies files, VS automatically checks them out.  We’ll need to see how that works for folks using different systems.

What’s new in this version?

The template on CodePlex (version 2.0.01 at the top of the file) supports what I described in my previous post, plus some new goodies.

Refactoring support for action methods

One of the big issues before was the lack of refactoring support.  e.g. when you wrote:

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

This looked like a call to you Details controller action, but it was actually an unrelated method by the same name.  Hence, if you renamed your action method and refactored, this call was not modified.  It would give a compile error, and had to be hand fixed.

Now the template takes a drastically different approach:

  • It extends the controller class
  • It overrides the action method (hence the need for it to be virtual!)
  • The override never calls the base (that would be very wrong), but instead returns a special ActionResult which captures the call (controller name, action name, parameter value).
  • The template emit a new RedirectToAction (or ActionLink, …) overload which understands this special ActionResults, and turns the call data into a ‘regular’ RedirectToAction call.

Pretty tricky stuff, but it works quite well.  Some credit to my manager Mike Montwill for coming up with this crazy idea!

Because the method you call is an override of the real action method, refactoring works perfectly.  Also, if you F12 (Go To Definition) on the call, it’ll go straight to your Action method and not some generated code.

Unfortunately, Visual Studio doesn’t support refactoring in Views, but 3rd party tools like Resharper and CodeRush do, so if you use one of those, you’re fully covered.

The T4 file automatically runs whenever you build

This was the other big painful issue I was up against: every time you made a change to your code that affect the generated code (e.g. new Action, new View, …), you had to manually save the .tt file to cause it to regenerate the new helper code.

This was a really hard issue, and I must warn you that what I ended up with is more of a workaround than a fix.  However, it is pretty effective, so until we find a better solution, it’ll have to do.

Here is how it works.  Warning: reading this has been shown to cause headaches in lab rats:

  • As part of its execution, the T4 file finds itself in the VS project system (it had to do that anyway)
  • It then runs the magic instruction ‘projectItem.Document.Saved = false;’, which causes it to become dirty.
  • It then proceeds to do its code generation, leaving its file in an unsaved state
  • Next time you Build your project, VS first saves all the files
  • This causes the ‘dirty’ T4 template to execute, mark itself as dirty again, and redo its code generation
  • You get the idea!  If you feel like the lab rats, this may help.

One caveat is that you have to initiate the cycle by opening and saving T4MVC.tt once.  After you do that, you don’t need to worry about it.

Support for strongly typed links to static resources

Credit for this idea goes to Jaco Pretorius, who blogged something similar.

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

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

You can now 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.

More consistent short form to refer to a View from a Controller class

Previously, it supported an _ based short form inside the controller:

return View(View_InvalidOwner);

That was a bit ugly.  Now, the short form is:

return View(Views.InvalidOwner);

Here, Views.InvalidOwner is the same as MVC.Dinners.Views.InvalidOwner, but can be shortened because ‘Views’ is a property on the controller.

Many bug fixes

I also fixed a number of bugs that people reported and that I ran into myself, e.g.

  • It supports controllers that are in sub-folders of the Controllers folder and not directly in there
  • It works better with nested solution folder

I’m sure there are still quite a few little bugs, and we’ll work through them as we encounter them

Posted by davidebb | 53 Comments
Filed under: , , ,

Mind if my MVC T4 template changes your code a bit?

Update: Please see this post for what came out of this ‘poll’, and for a pointer to the newest T4 template on CodePlex.

When working on my MVC T4 template, I was not able to use reflection to discover the Controllers and Actions, because the code that the template generates is itself in the same assembly as the controllers.  So that causes a bit of a chicken and egg problem.

Instead, I had to get out of my element and learn something I was not familiar with: the Visual Studio File Code Model API.  It’s very different from using reflection, because instead of working at the assembly level, you work at the source file level.

You have to first locate the source file you want to look into.  You can then ask for the namespaces that it contains, and the classes that they contain, and finally the various members in those classes.  To be honest, I find this API quite ugly.  It’s a COM interop thing with a horrible object model that looks like it grew organically from version to version rather than having been designed with usability in mind.  So all in all, I used it because I had to, but the whole time I was wishing I could use reflection instead.

But then I made an important realization.  Ugly as it is, this object model supports something that would never be possible with reflection: it lets me modify the source code!

If you look at my previous post, I wrote “But to make things even more useful in the controller, you can let the T4 template generate new members directly into your controller class.  To allow this, you just need to make you controller partial”.  And I have logic in the template that tests this, and does extra generation if it is partial, e.g.

if (type.ClassKind == vsCMClassKind.vsCMClassKindPartialClass) { ... }

But instead, I have now realized that I can turn this check into an assignment, and change the class to partial if it isn’t already!

type.ClassKind = vsCMClassKind.vsCMClassKindPartialClass;

Likewise, I have scenarios where I can do cool things if the Controller actions are virtual, and I can just change them to be with a simple line:

method.CanOverride = true;

To be clear, those statements actually modify the original source file, the one that you wrote.  While this is certainly powerful and opens up some new doors, it also raises a big question which is the main purpose of this post:

Do you mind if the T4 template makes these small changes to your code?

We’re only talking about pretty harmless things (making classes partial and methods virtual), but I know developers can get nervous if even small changes magically happen in their source files.

So please tell me how you feel about this, e.g. is it more:

  1. It’s harmless, go for it if it makes the template more useful
  2. Undecided. I don’t really like it, but maybe I’ll put up with it.
  3. No way I’ll use this template if it messes with my files in any way. I may even sue you!

Tell me where you stand, and please don't sue me.

Posted by davidebb | 40 Comments
Filed under: , , ,

After almost five years, my blog has a name!

So I have had this blog since October 2005, and the entire time it has been named “David Ebbo’s blog”.  There were a number of solid reasons that had led me to choose this catchy name:

  • It’s a blog
  • It’s mine
  • My name is David Ebbo

Last December, I blogged with great excitement that I had picked not a new title, but a new subtitle for it: “The Ebb and Flow of ASP.NET”.  It was a week before xmas, there was almost no one at work and I might have had a drink or two, but for whatever reason, I came up with that brilliant name.  But that was just the subtitle, which is displayed in a small font that no one reads, so it went quietly unnoticed.

Today, I am taking a much bigger step in my blogging career as I’m actually changing the blog title.  Yes, the very string that makes or breaks a blog, the one that shows up in your browser’s title bar, the one that drives how much advertisement money I would get if only I had ads on there!

And the new name is… <% Angle Bracket Percent %>!  You know, like those things we have all over aspx pages and MVC views.  Hmmm, if I need to explain it, then it may not be as good a name as I first thought.  Now leave me alone, or I’ll stop following you on Twitter.

Update: ok, it appears that having “<%” as part of the title was not such a great idea, as it shows up as &lt;% is various places, which looks ugly.  So I dropped the <% %> and only kept “Angle Bracket Percent”.  I’m not sure if that makes it more or less lame, nor am I sure if I care.

Posted by davidebb | 5 Comments

A new and improved ASP.NET MVC T4 template

Update: Please read this post for the newest and greatest.

A couple weeks ago, I blogged about using a Build provider and CodeDom to generate strongly typed MVC helpers at runtime.  I followed up a few days later with another version that used T4 templates instead, making it easier to customize.

And now I’m back with yet another post on this topic, but this time with a much simpler and improved approach!  The big difference is that I’m now doing the generation at design time instead of runtime.  As you will see, this has a lot of advantages.

Drawbacks of the previous runtime approach

Before we go and re-invent the wheel, let’s discuss what the issues with the runtime T4 approach were, and how this is solved by this new approach.

Complex configuration: to enable the runtime template, you had to add a DLL to your bin, modify two web.config files, and drop two T4 files in different places.  Not super hard, but also not completely trivial.  By contrast, with this new approach you just drop one .tt file at the root of your app, and that’s basically it.

No partial trust support: because it was processing T4 files at runtime, it needed full trust to run.  Not to mention the fact that using T4 at runtime is not really supported!  But now, by doing it at design time, this becomes a non-issue.

Only works for Views: because only the Views are compiled at runtime, the helpers were only usable there, and the controllers were left out (since they’re built at design time).  With this new approach, Controllers get some love too, because the code generated by the template lives in the same assembly as the controllers!

Let’s try the new T4 template with the Nerd Dinner app

Let’s jump right in and see this new template in action!  We’ll be using the Nerd Dinner app as a test app to try it on.  So to get started, go to http://nerddinner.codeplex.com/, download the app and open it in Visual Studio 2008 SP1.

Then, simply drag the T4 template (the latest one is on CodePlex) into the root of the NerdDinner project in VS.  And that’s it, you’re ready to go and use the generated helpers!

Once you’ve dragged the template, you should see this in your solution explorer:

image

Note how a .cs file was instantly generated from it.  It contains all the cool helpers we’ll be using!  Now let’s take a look at what those helpers let us do.

Using View Name constants

Open the file Views\Dinners\Edit.aspx.  It contains:

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

This ugly “DinnerForm” literal string needs to go!  Instead, you can now write:

<% Html.RenderPartial(MVC.Dinners.Views.DinnerForm); %>
Though it’s wordier, note that you get full intellisense when typing it.

ActionLink helpers

Now open Views\Dinners\EditAndDeleteLinks.ascx, where you’ll see:

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

Here we not only have a hard coded Action Name (“Delete”), but we also have the parameter name ‘id’.  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!

But with our cool T4 helpers, you can now change it to:

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

Basically, we got rid of the two unwanted literal strings (“Delete” 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.  And again, you get full intellisense:

 image

By the way, feel free to press F12 on this Delete() method call, and you’ll see exactly how it is defined in the generated .cs file.  The T4 template doesn’t keep any secrets from you!

Likewise, the same thing works for Ajax.ActionLink.  In Views\Dinners\RSVPStatus.ascx, change:

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

to just:

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

You can also do the same thing for Url.Action().

Even the controller gets a piece of the action

As mentioned earlier, Controllers are no longer left out with this approach.

e.g. in Controllers\DinnersController.cs, you can replace

return View("InvalidOwner");

by

return View(MVC.Dinners.Views.InvalidOwner);

But to make things even more useful in the controller, you can let the T4 template generate new members directly into your controller class.  To allow this, you just need to make you controller partial, e.g.

public partial class DinnersController : Controller {

Note: you now need to tell the T4 template to regenerate its code, by simply opening the .tt file and saving it.  I know, it would ideally be automatic, but I haven’t found a great way to do this yet.

After you do this, you can replace the above statement by the more concise:

return View(View_InvalidOwner);

You also get to do some cool things like we did in the Views.  e.g. you can replace:

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

by

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

 

How does the T4 template work?

The previous runtime-based T4 template was using reflection to learn about your controllers and actions.  But now that it runs at design time, it can’t rely on the assembly already being built, because the code it generates is part of that very assembly (yes, a chicken and egg problem of sort).

So I had to find an alternative.  Unfortunately, I was totally out of my element, because my expertise is in the runtime ASP.NET compilation system, while I couldn’t make use of any of it here!

Luckily, I connected with a few knowledgeable folks who gave me some good pointers.  I ended up using the VS File Code Model API.  It’s an absolutely horrible API (it’s COM interop based), but I had to make the best of it.

The hard part is that it doesn’t let you do simple things that are easy using reflection.  e.g. you can’t easily find all the controllers in your project assembly.  Instead, you have to ask it to give you the code model for a given source file, and in there you can discover the namespaces, types and methods.

So in order to make this work without having to look at all the files in the projects (which would be quite slow, since it’s a slow API), I made an assumption that the Controller source files would be in the Controllers folder, which is where they normally are.

As for the view, I had to write logic that enumerates the files in the Views folder to discover the available views.

All in all, it’s fairly complex and messy code, which hopefully others won’t have to rewrite from scratch.  Just open the .tt file to look at it, it’s all in there!

In addition to looking at the .tt file, I encourage you to look at the generated .cs file, which will show you all the helpers for your particular project.

Known issues

T4 file must be saved to regenerate the code

This was briefly mentioned above.  The T4 generation is done by VS because there is a custom tool associated with it (the tool is called TextTemplatingFileGenerator – you can see it in the properties).  But VS only runs the file generator when the .tt file changes.  So when you make code changes that would affect the generated code (e.g. add a new Controller), you need to explicitly resave the .tt file to update the generated code.  As an alternative, you can right click on the .tt file and choose “Run Custom Tool”, though that’s not much easier.

Potentially, we could try doing something that reruns the generation as part of a build action or something like that.  I just haven’t had time to play around with this.  Let me know if you find a good solution to this.

No refactoring support

This was also the case with the previous template, but it is worth pointing out.  Because all the code is generated by the T4 template, that code is not directly connected to the code it relates to.

e.g. the MVC.Dinners.Delete() generated method results from the DinnersController.Delete() method, but they are not connected in a way that the refactoring engine can deal with.  So if you rename DinnersController.Delete() to DinnersController.Delete2(), MVC.Dinners.Delete() won’t be refactored to MVC.Dinners.Delete2().

Of course, if you resave the .tt file, it will generate a MVC.Dinners.Delete2() method instead of MVC.Dinners.Delete(), but places in your code that call MVC.Dinners.Delete() won’t be renamed to Delete2.

While certainly a limitation, it is still way superior to what it replaces (literal strings), because it gives you both intellisense and compile time check.  But it’s just not able to take that last step that allows refactoring to work.

It is worth noting that using Lamda expression based helpers instead of T4 generation does solve this refactoring issue, but it comes with a price: less natural syntax, and performance issues.

Final words

It has been pretty interesting for me to explore those various alternative to solve this MVC strongly typed helper issue.  Though I started out feeling good about the runtime approach, I’m now pretty sold on this new design time approach being the way to go.

I’d be interested in hearing what others think, and about possible future directions where we can take this.

Posted by davidebb | 40 Comments
Filed under: , , ,

A T4 based approach to creating ASP.NET MVC strongly typed helpers

Update: Please see this newer post for the latest and greatest MVC T4 template

 

Earlier this week, I wrote a post on using a BuildProvider to create ActionLink helpers.  That approach was using CodeDom to generate the code, and there was quite some user interest in it (and Phil blogged it, which helped!).

Then yesterday, I wrote a post on the Pros and Cons of using CodeDom vs T4 templates for source code generation.  They are drastically different approaches, and while both have their strengths, T4 has definitely been getting more buzz lately.

The logical follow-up to those two posts is a discussion on using T4 templates to generate MVC strongly typed helpers.  The general idea here is to use the existing ASP.NET extensibility points (BuildProvider and ControlBuilder), but rely on T4 templates to produce code instead of CodeDom.  Hence, I called the helper library AspNetT4Bridge (I’m really good at naming things!).

As far as I know, this is the first time that T4 templates are executed dynamically inside an ASP.NET application, so let’s view this as an experiment, which has really not been put to the test yet.  But it is certainly an exciting approach, so let’s see where it takes us!

 

What scenarios are we enabling

Before we get too far into the technical details, let’s look at the various MVC scenarios that this is enabling.  But the great thing to keep in mind is that since T4 templates are so flexible, it is very easy to tweak the generated code or add additional scenarios.  Hence it is best to view those scenarios as examples of the type of things that you can achieve with this model, rather than as a final end result.

ActionLink helpers

First, we have the same scenario that my previous post covered, except that we’re now using T4 instead of CodeDom.  Instead of writing:

<%= Html.ActionLink("Edit", "Edit", new { id = item.ID })%>

You can write the more strongly typed (here, ‘Test’ is the name of the controller):

<%= Html.ActionLinkToTestEdit("Edit", item.ID) %>

And note that you get full intellisense in VS for those helpers!

image

Action Url helpers

This is similar to the previous section, except it covers the case where you need to generate raw URL’s rather than HTML <a> tags.  Instead of writing:

<%= Url.Action("Edit", new { id = item.ID }) %>

You can write:

<%= Url.UrlToTestEdit(item.ID) %>

Constants for View Names

That’s another case where you often end up hard coding view names as literal string, e.g.

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

With the generated helpers, you get to write:

<% Html.RenderPartial(Views.Shared.LogOnUserControl); %>

And again, you get full intellisense:

image

Rendering helpers for strongly typed views

Assume you are using a strongly typed view.  You might write something like this (which is what the View wizard generates by default):

<label for="Name">Name:</label>
<%= Html.TextBox("Name", Model.Name) %>
<%= Html.ValidationMessage("Name", "*") %>

With the helpers, you can instead write:

<%= ViewModel.Name.Label() %>
<%= ViewModel.Name.TextBox() %>
<%= ViewModel.Name.Validation() %>

Here ViewModel is some name I came up with (well, Fowler did!) as the class that holds the helpers.  There may be better names for it!

Note that the helpers can be smart about what they offer based on the data type.  e.g. for a boolean field, you typically want a check box instead of a text box, and the helpers account for this:

image

 

What do you need to do to enable this in your MVC app?

There are five relatively straightforward steps you need to take to enable this.

1. Copy AspNetT4Bridge.dll into the bin folder of your app (you’ll get it by building the attached sample).

2. In the web.config file at the root of your app, register the BuildProvider for .tt files

<compilation>

... <buildProviders> <add extension=".tt" type="AspNetT4Bridge.AspNetT4BridgeBuildProvider"/> </buildProviders> </compilation>

3. In the web.config in the Views folder (which you didn’t know existed, did you?), change the pageParserFilterType as follows:

pageParserFilterType="AspNetT4Bridge.Mvc.ViewTypeParserFilter"

4. Copy the Templates and App_Code folders (each containing a .tt file) from the attached sample app to the root of your app. In VS, blank out the Custom Tool for the .tt files if you see it set to TextTemplatingFileGenerator (see this post for some details on this).

5. If you have any strongly typed views (i.e. views the inherit ViewPage<T> and not just ViewPage), you need to change the Inherits attribute from System.Web.Mvc.ViewPage<...> to AspNetT4Bridge.Mvc.ViewPage<...>.  I know that’s a bit ugly, and we can think of ways to clean that up later.

 

So what about those T4 templates?

This post is supposed to be about using T4 templates, and so far we haven’t said a whole lot about them.  They are certainly the magic piece that makes all this work.  We are actually using two different .tt files, which cover two distinct scenarios:

Top level code

This applies to generated code that every page is referencing, and is used for the first three scenarios above (Action Links, Urls and View name constants).  The T4 file that does this is App_Code\StrongTypedLinkExtensions.tt.

As an aside, many people have a misconception that App_Code is just for web sites and not for web apps.  However, while it is rarely used, is is fully available.  It’s built with a reference to the bin assemblies, and all pages are built to a reference to it.

The way we make App_Code work with T4 templates is that we have a BuildProvider registered for the .tt extension.  This BuildProvider uses its own ITextTemplatingEngineHost in order to process the T4 templates into source code.  It then uses a CodeDom CodeSnippetCompileUnit to wrap this code into something CodeDom understands.  That’s why I called this AspNetT4Bridge, as it really bridges the CodeDom world of ASP.NET with the T4 world.  Fine, you come up with a better name! :)

Look for this logic in AspNetT4BridgeBuildProvider.cs.

Page level code

This applies to code that is specific to a given page, and that we just want to inject in there.  This is what’s uses for the view helpers like ViewModel.Name.TextBox() described above.  The T4 file that does this is Templates\MvcHtmlRenderHelpers.tt.

This works by using a ControlBuilder and its ProcessGeneratedCode method, which I discussed in a previous post.  The situation is similar to the App_Code case: we need to come up with some source code, we use T4 to generate it, and we wrap it in a CodeDom construct (in this case it’s a CodeSnippetTypeMember, because the code goes inside the class).

Look for this logic in T4CodeGenerator.cs.

But what the heck is in those T4 templates?

So I’ve talked about how they get hooked up and executed, but still haven’t said a word about what’s in them.  Well, I told you where they are, so go ahead and take a look!  e.g. check out App_Code\StrongTypedLinkExtensions.tt.

There is nothing really hard about how it works, but the mix of generator code and generated code certainly makes it confusing at first.  Do yourself a favor and install the free Clarius Community T4 editor.  It doesn’t do a whole lot, but the fact that it shows the two types of code (generator and generated) in different color goes a long way to make things clearer.

The way the Action Links and Url helpers are generated is fairly straightforward:

  • It gets the list of referenced assemblies from the Host
  • It uses reflection to figure out what types are controllers
  • For each controllers, it finds that Action methods
  • For each Action method, it generates both an ActionLine and a Url helper, custom made for that action method (hence strongly typed).

The View Name Constant helpers are also fairly simple.  They just look at what directories and files are under the Views folder, and generate constants accordingly.  I’ll show this part here as an example:

namespace Views {
<#
var viewsDir = HostingEnvironment.VirtualPathProvider.GetDirectory("~/Views");
    foreach (VirtualDirectory dir in viewsDir.Directories) {
#>
    public static class <#= dir.Name #> {
<#      foreach (VirtualFile file in dir.Files) {
            string viewName = Path.GetFileNameWithoutExtension(file.Name);
#>
        public const string <#= viewName #> = "<#= viewName #>";
<#      } #>
    }
    
<#  } #>
}

 

Go ahead, try changing them!

One of the big reason to use T4 over CodeDom for this code generation is to not ‘lock’ the kind of code that get generated into a binary.  Instead, you can very easily tweak it, remove things you don’t care about, and generate entirely new things.  All this really takes is an understanding of <# #> and <#= #> blocks, which is pretty easy when you already know how <% %> and <%= %> blocks work, since they’re pretty much the same thing!

 

Final thoughts

So here we are, dynamically executing T4 templates at runtime in an ASP.NET app.  One big caveat that I mentioned in my previous post is that you’re not really supposed to do that!  Copying from there:

CodeDom is part of the framework, while T4 is not.  That means that if you need to dynamically do this at runtime (as opposed to within VS), then T4 is not really an option.  Technically, it is possible to use it at runtime outside VS, but you either have to run on a machine with VS installed, or you have to copy Microsoft.VisualStudio.TextTemplating.dll into your project (which works, but is not officially supported – hopefully it will be at some point!).

So I wrote that it wasn’t an option, and next thing I blog about doing it.  What’s the deal?  Well, there are some things that are changing in VS 2010 that will help.  In particular, they are adding the concept of preprocessing T4 templates, while today you can only fully process them all at once.  With preprocessing, we’ll be able to:

  • Let VS preprocess the T4 templates into some intermediate source code (that itself doesn’t requite the T4 runtime.
  • Run this code at ASP.NET runtime to generate the code we care about and add it to the compilation (e.g. in App_Code as above).  This takes no dependency on the T4 runtime.

Another caveat I need to mention is that T4 templates can’t be fully executed in medium trust (because they need to compile code).  However, with the VS 2010 changes, that will no longer be an issue.

So while in the short term, we are breaking the rules a bit by doing this and there are some tough edges, it demonstrates some very useful concepts that can be fully supported later.

And if all else fails, I had a lot of fun playing with it all!

Posted by davidebb | 12 Comments
Filed under: , ,

Attachment(s): AspNetT4Bridge.zip
More Posts Next page »
 
Page view tracker