IDisposable objects with StructureMap and ASP.NET MVC 4

I’ve recently discovered a bit of an issue with running an IoC container with ASP.NET MVC’s IDependencyResolver. If you have a controller that has dependencies on things that implement IDisposable then the dispose method was not being called.

Apparently, if the controller itself is disposable then MVC will clean that up and that can obviously clean up any dependencies that it created and are also disposable. However, if you are injecting the dependency then the controller should not really be disposing of those dependencies because it did not create them as it has no knowledge of the lifecycle of those objects – the owner (the object that created the dependency) is really responsible for disposing of its objects.

So, the responsible party for disposing of the is what ever created it. However, in MVC 4 the Service Locator has no way of disposing downstream objects that get created when instantiating the controller, it only deals with the controller directly, so if a downstream object that the controller depends on needs to be disposed then the IoC container has to manage that. Mike Hadlow has a much better explanation of what is going on here and his dealings with using, specifically, Castle Windsor and the IDepenencyResolver.

Since I’m using StructureMap, it does have a way of helping you clean up.

For example, in the Initialisation expression that the ObjectFactory.Initialize uses I’ve got a repository set up like this:

x.For<IRepository>().HttpContextScoped().Use<Repository>();

This creates a new Repository for each request that the MVC application receives. However, this on its own is not enough because it means that while each request gets a new repository, none of the resources of these repository objects are being cleaned up because it never releases them. Eventually those resources will run out, be they database connections, file handles, or what ever the repository needs to use.

You can put in your Global.asax.cs file a method called Application_EndRequest() which is called at the end of each request. Or, if you already have one you can simply add this line of code to it.

protected void Application_EndRequest()
{
  ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}

Getting umbraco up and running in with MVC 4

In this post, I’ll look at getting Umbraco and MVC to play nice with each other in the same project.

Installing Umbraco 4.8

First off create a Web Application project in Visual Studio. For this example, I’m just going to create the project as “UmbMvc”.

Visual Studio 2010: New Project

Visual Studio 2010: New Project

Once Visual Studio has created the project, delete most of its content. We’re doing this because we don’t have fully empty projects. If you were doing this in VS 2012 you could have selected the Empty Web Application project instead.

The Solution explorer should look like this when your done:

Visual Studio 2010: Solution Explorer

Visual Studio 2010: Solution Explorer

Next up, Umbraco has to be installed. This can be done with NuGet. I used the Package Manager Console, which can be accessed from the Tools menu:

Visual Studio: NuGut Package Manager Console

Visual Studio: NuGut Package Manager Console

Then typed Install-Package UmbracoCms to install the package and its dependencies. The output looks like this:

PM> Install-Package UmbracoCms
'UmbracoCms.Core (= 4.8.0)' not installed. Attempting to retrieve dependency from source...
Done.
Successfully installed 'UmbracoCms.Core 4.8.0'.
Successfully installed 'UmbracoCms 4.8.0'.
Successfully added 'UmbracoCms.Core 4.8.0' to UmbMvc.
'web.config' already exists. Skipping...
Successfully added 'UmbracoCms 4.8.0' to UmbMvc.

Don’t worry about the message about web.config. It will write the necessary detail into the web.config file for you.

If you prefer to use the NuGet dialog, you can search for “UmbracoCms” and install the package from there. It will download and install the dependencies for you there too.

NuGet Package Manager Dialog

NuGet Package Manager Dialog

At this point you can run up Umbraco to configure it and set the databases up and so on. When you’ve finished this process you’ll arrive at the Umbraco administration area. At this point you want to stop the app from running in Visual Studio.

Wiring up MVC 4

Next up is to get MVC installed. For this I’m taking the advice on Aaron Powell’s blog, so go visit there for the detail. (Start at the section marked “Getting MVC installed”). I’ve added my own notes below for some differences I found between our experiences.

Installing ASP.NET at the time of writing installs MVC 4:

PM> install-package microsoft.aspnet.mvc
'Microsoft.AspNet.WebPages (= 2.0.20505.0)' not installed. Attempting to retrieve dependency from source...
Done.
'Microsoft.Web.Infrastructure (= 1.0.0.0)' not installed. Attempting to retrieve dependency from source...
Done.
'Microsoft.AspNet.Razor (= 2.0.20505.0)' not installed. Attempting to retrieve dependency from source...
Done.
Successfully installed 'Microsoft.Web.Infrastructure 1.0.0.0'.
Successfully installed 'Microsoft.AspNet.Razor 2.0.20505.0'.
Successfully installed 'Microsoft.AspNet.WebPages 2.0.20505.0'.
Successfully installed 'Microsoft.AspNet.Mvc 4.0.20505.0'.
Successfully added 'Microsoft.Web.Infrastructure 1.0.0.0' to UmbMvc.
Successfully added 'Microsoft.AspNet.Razor 2.0.20505.0' to UmbMvc.
Successfully added 'Microsoft.AspNet.WebPages 2.0.20505.0' to UmbMvc.
Successfully added 'Microsoft.AspNet.Mvc 4.0.20505.0' to UmbMvc.

Because of the way paths work in Umbraco, I was either going to have to reserve every controller and area name in the umbracoReservedPaths configuration element, or create a prefix. I decided it was probably best to create a prefix, that way I only have to modify the config once and everything else simply works after that. So, my RouteSetup class looked like this:

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

[assembly: PreApplicationStartMethod(typeof(UmbMvc.App_Start.RouteSetup), "Setup")]
namespace UmbMvc.App_Start
{
  public class RouteSetup
  {
     public static void Setup()
     {
       RouteTable.Routes.MapRoute(
         "Default", // Route name
         "x/{controller}/{action}/{id}", // URL with parameters
         new {controller = "Home", action = "Index", id = UrlParameter.Optional} // Parameter defaults
         );
     }
  }
}

Note the “x” at the start of the URL part of the route.

My umbracoReservedPaths config element now looks like this:

<!-- Remember to add into the umbracoReservedPaths every route that MVC wants to take. 
     It may be better to create a prefix so you only have to do this the once.-->
<add key="umbracoReservedPaths" value="~/umbraco,~/install/,~/x" />

Fixing up the web.config file

From Aaron’s blog post, I still couldn’t quite get it to work. I got an error message that read: Compiler Error Message: CS0234: The type or namespace name ‘Helpers’ does not exist in the namespace ‘System.Web’ (are you missing an assembly reference?)

I found that I needed to add the following to the <assemblies> section of the web.config file:

<add assembly="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

That helped, but I was still getting error an error message. This time it was Compiler Error Message: CS0234: The type or namespace name ‘Ajax’ does not exist in the namespace ‘System.Web.Mvc’ (are you missing an assembly reference?)

I found that I also needed to set the reference to System.Web.Mvc to “Copy Local”

Visual Studio 2010: System.Web.Mvc Copy Local

Visual Studio 2010: System.Web.Mvc Copy Local

Then when I ran the application and went to the URL http://localhost:60445/x/Home, I got a page back that said: Hello I’m a razor view.

This is finally what I expected.

Fixing up the project type.

One last thing, Aaron also mentions that you’ll get various errors in the views in Visual Studio because the project type was a Web Application not an MVC Web Appliction project. Although it doesn’t stop the application from running correctly, it is very disconcerting to see. He doesn’t give a solution to that. To solve this, you need to add a GUID in the csproj file.

To do this, you need to right click on the web project and click “Unload Project”, when it has unloaded, right-click again and click “Edit xxx.csproj”.

Look for the element named “ProjectTypeGuids” and add in the guid: {E3E379DF-F4C6-4180-9B81-6769533ABE47}. The whole line should now read:

<ProjectTypeGuids>{E3E379DF-F4C6-4180-9B81-6769533ABE47};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Save the file, then right-click the project in the solution and click “Reload project”. It will prompt you to close the text version of the project file, and then the project will be loaded back. Now you should not have any issues with the views finding false errors, such as not being able to resolve ViewBag.

Follow

Get every new post delivered to your Inbox.