Pages

Tuesday 6 April 2010

Adding a Lazy Loading Pattern to T4MVC

I added T4MVC to our ASP.NET MVC application yesterday. Great idea - don't know why it's taken me so long to get 'round to it? Unfortunately, a number of my unit tests were now failing with a System.NotImplementedException being thrown.

Some of our controllers are currently redundant so I changed the base class' constructor to make sure no-one called them accidentally...

public abstract class MyRedundantBaseClass
  {
    public MyRedundantBaseClass()
    {
      throw new NotImplementedException();
    }
  }

Now, none of my tests go anywhere near this class (or any inherited ones) so why was it throwing the exceptions?

The problem is in one of the auto-generated classes in the T4MVC.cs file. The template generates a static field for each non-abstract controllers in your application...

public static class MVC
  {
    public static MyApp.Controllers.Account.AccountController Account = new MyApp.Controllers.Account.T4MVC_AccountController();
    // Repeated for each controller...
  }

This list of fields also included a number that extended my redundant class. So, as soon as I did anything with the T4MVC framework all these classes got automatically instantiated and now I'm dead in the water.

My solution is to edit the template so that it generates a Lazy Loading version of the T4MVC.cs file. This also makes it a little more efficient as it will only new-up classes when it needs them.

Here's the old template section (starting from line 73)...

<#foreach (var controller in DefaultArea.GetControllers()) { #>
    public static <#=controller.FullClassName #> <#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
<#} #>

... and here's what I changed it to...

<#foreach (var controller in DefaultArea.GetControllers()) { #>
 private static <#=controller.FullClassName #> _<#=controller.Name #>;
<#} #>
<#foreach (var controller in DefaultArea.GetControllers()) { #>
 public static <#=controller.FullClassName #> <#=controller.Name #>
 {
  get
  {
   if (_<#=controller.Name #> == null)
    _<#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
    
   return _<#=controller.Name #>;
  }
 }
<#} #>

This now generates the following code in T4MVC.cs...

public static class MVC
  {
    private static MyApp.Controllers.Account.AccountController _Account;

    public static MyApp.Controllers.Account.AccountController Account
    {
      get
      {
        if (_Account == null)
          _Account = new MyApp.Controllers.Account.T4MVC_AccountController();

        return _Account;
      }
    }

    // Repeated for each controller...
  }

I still need to add a double locking mechanism for thread safety, but I think this makes a valuable improvement to an already great framework.

1 comment:

  1. Good idea, let's get this change in. Let's discuss by email offline. Thanks!

    ReplyDelete