Click here to Skip to main content
15,860,859 members
Articles / Web Development / ASP.NET

PartCreationPolicyAttribute, controller lifespan, and MEF in ASP.NET MVC 3

Rate me:
Please Sign up or sign in to vote.
4.80/5 (4 votes)
31 Aug 2011CPOL5 min read 19.7K   10   1
This article discusses how you may handle the lifespan of a controller when you use the Managed Extensibility Framework (MEF) in ASP.NET MVC 3 applications. You'll benefit from this article if you have some understanding and knowledge of MEF.

Introduction

This article discusses how you may handle the lifespan of a controller when you use the Managed Extensibility Framework (MEF) in ASP.NET MVC 3 applications. You’ll benefit from this article if you have some understanding and knowledge of MEF and you’ve read ExportAttribute, ImportAttribute, CompositionContainer, and MEF in ASP.NET MVC 3. The article does not deal with the intricacies of MEF or the ASP.NET MVC 3 system.

The story is accompanied with downloadable Visual Studio solutions:

In August 2011, you should consider the following software:

  • Visual Studio 2010
  • Visual Studio 2010 SP1
  • .NET Framework 4.0
  • ASP.NET MVC 3
  • C# 4.0.

If you run a plain vanilla ASP.NET MVC 3 application, whenever an HTTP request is made, for example when a user clicks a link on the screen, the ASP.NET MVC 3 system creates a fresh instance of the specified controller class. This fact often eludes an unsuspecting developer when he utilizes MEF in his application. The lack of foresight manifests itself with a rude message saying “a single instance of controller ‘xxx’ cannot be used to handle multiple requests”.

Figures 1 and 2 illustrate what happens if you use MEF, but you don’t handle the lifespan of your controller properly.

Figure 1 — ASP.NET MVC 3 application with MEF. A user has requested the Index page.

figure-1.png

MEF creates an instance of the HomeController class. The ASP.NET MVC 3 system emits HTML and sends it to the user’s browser. The user clicks the Create New link. The click results in a new HTTP request. To show the Create page, the ASP.NET MVC 3 system needs a fresh instance of the HomeController class, but MEF hasn’t produced a new controller object. In Figure 2, the ASP.NET MVC 3 system responds to the user’s click, reflecting this flaw.

Figure 2 — ASP.NET MVC 3 application with MEF. Error message in response to user’s click.

figure-2.png

You can download the Mvc3MefLifespanError solution that demonstrates the problem of incorrect management of the controller’s lifespan.

You have at least two options to rectify the problem in MEF:

  • You use PartCreationPolicyAttribute; or
  • You alternate the construction and destruction of a composition container at each page request.

Option 1 — PartCreationPolicyAttribute usage

In Example 1, I mark the HomeController class with PartCreationPolicyAttribute(CreationPolicy.NonShared). CreationPolicy.NonShared instructs MEF that it should create a new instance of the HomeController class whenever the application needs a HomeController object.

Example 1 — PartCreationPolicyAttribute adorns the HomeController class.
C#
[
  ExportAttribute
  ,
  PartCreationPolicyAttribute(CreationPolicy.NonShared)
]
public class HomeController : Controller
{
  [ImportingConstructorAttribute]
  public HomeController(MessageSource messageSource)
  {
    this.messageSource = messageSource;
  }
  .
  .
  .
}

The complete Visual Studio Mvc3MefLifespanPartCreationPolicy solution demonstrates the creation policy attribute.

There is widespread belief among MEF developers that, by default, a composition container creates, in Microsoft parlance, single parts or singleton objects. The truth is that the default creation policy is CreationPolicy.Any. However, there is a trick to CreationPolicy.Any. If the importing entity doesn’t specify its preference, the composition container produces a single object.

This explains why I get the error (see Figure 2) when I run the Mvc3MefLifespanError sample solution. Since I don’t specify a creation policy, the policy is CreationPolicy.Any, by default. However, in the absence of the importing entity, the composition container creates a single instance of the HomeController class and supplies it to the ASP.NET MVC 3 system for each HTTP page request.

Option 2 — Construction and destruction of a composition container at each page request

When the ASP.NET MVC 3 system processes an HTTP request, it asks your custom dependency resolver to fetch objects from your MEF composition container. When you fetch an object from the container, the container satisfies all imports with relevant exports.

I avail myself of this feature by providing methods that will respond to the HttpApplicantion.BeginRequest and HttpApplicantion.EndRequest events.

In Example 2, I present a different implementation of the IDependencyResolver interface from the one I show in Example 12 of the ExportAttribute, ImportAttribute, CompositionContainer, and MEF in ASP.NET MVC 3 article. Instead of a private variable that is initialized in the constructor, I have a static CompositionContainer property.

Example 2 — MefDependencySolver class with the static CompostionContainer property.
C#
namespace MefLifespanMvc03
{
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.ComponentModel.Composition.Hosting;
  using System.Web.Mvc;

  public class MefDependencySolver : IDependencyResolver
  {
    public static CompositionContainer CompositionContainer { get; set; }


    public object GetService(Type serviceType)
    {
      string name = AttributedModelServices.GetContractName(serviceType);
      return CompositionContainer.GetExportedValueOrDefault<object>(name);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
      return CompositionContainer.GetExportedValues<object>(serviceType.FullName);
    }
  }
}

In Example 3, I define the Application_BeginRequest method in the MvcApplication class. When an HTTP request is initiated, the ASP.NET MVC 3 system invokes the method, setting the CompositionContainer property of my custom dependency resolver to a newly created instance of a CompositionContainer.

Example 3 — Creates a new instance of CompositionContainer.
C#
protected void Application_BeginRequest()
{
  TypeCatalog typeCatalog = new TypeCatalog(typeof(HomeController),
                                            typeof(MessageSource));
  MefDependencySolver.CompositionContainer = 
    new CompositionContainer(typeCatalog);
}

In Example 4, I define the Application_EndRequest method in the MvcApplication class. When the HTTP request has completed, the ASP.NET MVC 3 system invokes the method, destructing the CompositionContainer of my custom dependency resolver.

Example 4 — Destroys the CompositionContainer.
C#
protected void Application_EndRequest()
{
  MefDependencySolver.CompositionContainer.Dispose();
}

The technique of constructing/destructing a composition container allows you to ignore MEF part creation policies, but it requires that you write more code in the MvcApplication class. The technique has an additional advantage, to wit, it prevents memory leaks that may occur very easily if you are not careful.

I’ve enclosed the complete Mvc3MefLifespanCompositionContainer solution to demonstrate the construction/destruction technique, in the downloadable package.

Summary

If you use MEF in your ASP.NET MVC 3 application, beware of its pitfalls. You need to handle the lifespan of your controllers. The article demonstrates two techniques. You can use PartCreationPolicyAttribute, or create and dispose the composition container as your application responds to BeginRequest and EndRequest events. A nice thing about the creation/disposal technique is that you also avoid potential memory leaks.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionFatal flaw in Option 2 Pin
Member 324818918-Jul-12 12:50
Member 324818918-Jul-12 12:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.