Introduction
This article illustrates the usage of the Managed Extensibility Framework (MEF) in ASP.NET MVC 3 applications. You’ll benefit by this article if you have some understanding and knowledge of MEF. The article does not deal with the intricacies of MEF or the ASP.NET MVC 3 system.
The story is accompanied with a downloadable package that contains two Visual Studio solutions. The ClassicMvc01 solution is a simple ASP.NET MVC 3 application that displays text produced by a class. The MefMvc01 solution is a remake of the classic application, employing MEF constructs and requisite MVC 3 mechanics.
PartCreationPolicyAttribute, controller lifespan and MEF in ASP.NET MVC 3 is the follow-up to this article.
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.
The article has two sections. Section 1 presents ExportAttribute
, ImportAttribute
and CompositionContainer
. Section 2 discusses the usage of MEF in the ASP.NET MVC 3 system.
Section 1 MEF Basics
Microsoft people treat the abbreviation MEF as an acronym. Consequently, you pronounce MEF similarly to the word “deaf” and, in writing or speech, you don’t precede MEF with the definite article.
From Microsoft’s point of view, MEF is not an inversion-of-control system. However, MEF provides capabilities of an inversion-of-control system.
Three essential constructs in MEF are ExportAttribute
, ImportAttribute
and CompositionContainer
.
You use an ExportAttribute
to mark the following pieces of code:
- class
- field
- property
- indexer
- method
You use an ImportAttribute
to mark the following pieces of code:
- field
- property
- indexer
- argument.
Example 1 — A class marked with an ExportAttribute
[ExportAttribute]
public class A
{
public void ShowMessage()
{
Console.WriteLine("this is class A");
}
}
Example 2 — A property marked with an ImportAttribute
public class B
{
[ImportAttribute]
public A PropertyA { get; set; }
}
Example 3 — A method marked with an ExportAttribute
public class C
{
[ExportAttribute]
public void DoSomething()
{
}
}
MEF people at Microsoft love the word “part.” They talk, for example, of a discoverable part, a composed part, an exported part, etc. They’ve never defined clearly what a “part” means, but for all practical purposes, you may think of a part as a class that has at least one ExportAttribute
. In this spirit, class B of Example 2 is not a part, but class A of Example 1 and class C of Example 3 are parts.
Another MEF construct is the CompositionContainer
class. You supply the CompositionContainer
with code that you have marked with an ExportAttribute
or ImportAttribute
. The CompositionContainer
tries to match exports with imports. If you fed class A of Example 1 and class B of Example 2 to a CompositionContainer
, the CompositionContainer
would match the PropertyA
of the B class with the A class.
Example 4 demonstrates a complete program that defines classes A and B marked appropriately with an ExportAttribute
or ImportAttribute
. The CompositionContainer
receives instances of A and B classes. The CompositionContainer
composes the instances, i.e. it fulfills imports with exports. The CompositionContainer
returns a composed part. I’ve chosen to implement Example 4 in a console application. Unlike an ASP.NET MVC 3 application, a console application requires only one file. This allows you to see easily the three MEF constructs in action.
Example 4 — Console application illustrates usage of MEF basics
namespace MefExample4
{
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
[ExportAttribute]
public class A
{
public void ShowMessage()
{
Console.WriteLine("this is class A");
}
}
[ExportAttribute]
public class B
{
[ImportAttribute]
public A PropertyA { get; set; }
}
class Program
{
static void Main(string[] args)
{
CompositionContainer compositionContainer = new CompositionContainer();
compositionContainer.ComposeParts(new A(), new B());
B b = compositionContainer.GetExportedValueOrDefault<B>();
b.PropertyA.ShowMessage();
}
}
}
ExportAttribute
, ImportAttribute
and CompositionContainer
are essential constructs in MEF. With these three constructs, you can accomplish 70% of your work.
Section 2- MEF in ASP.NET MVC 3
Before I discuss MEF in the context of the ASP.NET MVC 3 system, I invite you to take a look at Figure 1. You’re an experienced developer and you can guess the structure of the application in Figure 1.
To make sure we are on the same wavelength, I show you essential pieces of code (Examples 5, 6 and 7) that implement what you see in the picture in a classic ASP.NET MVC 3 fashion.
Figure 1 — ASP.NET MVC 3 application. Text within a red border originates in a separate class.
Text “this message is from MessageSource” surrounded by a red border comes from class MessageSource
in Example 5. In the classic ASP.NET MVC 3 system, the HomeController
in Example 6 creates an instance of the class and sends it to the Index view in Example 7.
Example 5 — Classic ASP.NET MVC 3. Class MessageSource
is the origin of text in the Index view
namespace ClassicMvc01
{
public class MessageSource
{
public MessageSource()
{
this.Message = "this message is from MessageSource";
}
public string Message { get; private set; }
}
}
Example 6 — Classic ASP.NET MVC 3. HomeController
creates an istance of class MessageSource
namespace ClassicMvc01.Controllers
{
using System.Web.Mvc;
public class HomeController : Controller
{
private MessageSource messageSource = new MessageSource();
public ActionResult Index()
{
return View(this.messageSource);
}
}
}
Example 7 — Classic ASP.NET MVC 3. The Index view displays the message from MessageSource
@model ClassicMvc01.MessageSource
@{ViewBag.Title = "Index";}
<h2>Home/Index</h2>
<p>@Model.Message</p>
I’ve included the complete Visual Studio solution named ClassicMvc01
in the downloadable package.
I will now show how you implement Figure 1 in an ASP.NET MVC 3 application using MEF. In the downloadable package, you can use MefMvc01
for reference.
If you want to use MEF in your ASP.NET MVC 3 application, you should do five things:
- You mark pieces of code that MEF should take care of with
ExportAttributes
and ImportAttributes
.
- You create an instance of a
CompositionContainer
. You supply it with your marked code.
- You implement the
IDependencyResolver
interface.
- You supply the object that implements the
IDependencyResolver
interface with the instance of the CompositionContainer
.
- You register your object that implements the
IDependencyResolver
with the ASP.NET MVC 3 system.
In Visual Studio 2010, I start by creating an empty ASP.NET MVC 3 project. I add a reference to the System.ComponentModel.Composition
component.
1. You mark pieces of code that MEF should take care of with ExportAttributes and ImportAttributes.
I want MEF to take care of my HomeController
and MessageSource
class by creating an instance of class MessageSource
and setting the HomeController
’s messageSource
field to that instance.
I define class MessageSource
in Example 8. Note the difference between code in Example 5 and Example 8. In Example 8,
- I have a
using System.ComponentModel.Composition
directive, and
- I’ve marked class
MessageSource
with an ExportAttribute
.
Example 8 — Class MessageSource
is marked with ExportAttribute
namespace MefMvc01
{
using System.ComponentModel.Composition;
[ExportAttribute]
public class MessageSource
{
public MessageSource()
{
this.Message = "this message is from MessageSource";
}
public string Message { get; private set; }
}
}
I create a bare-bones HomeController
in Example 9. In the HomeController
, you see:
using System.ComponentModel.Composition
directive
- the
HomeController
class is marked with ExportAttribute
- the
messageSource
field is marked with ImportAttribute
Example 9 — Class HomeController
is marked with ExportAttribute
namespace MefMvc01.Controllers
{
using System.ComponentModel.Composition;
using System.Web.Mvc;
[ExportAttribute]
public class HomeController : Controller
{
[ImportAttribute]
private MessageSource messageSource;
public ActionResult Index()
{
return View(this.messageSource);
}
}
}
At runtime, an instance of MessageSource
will become a value that MEF will set to the messageSource
field, i.e. MEF imports the MessageSource
exported part to the messageSource
field.
Finally, I create an Index
view for the HomeController
in Example 10. MEF has nothing to do here. Code is nearly identical to that in Example 7.
Example 10 — Index views in the classic ASP.NET MVC 3 system and ASP.NET MVC with MEF are same
@model MefMvc01.MessageSource
@{ViewBag.Title = "Index";}
<h2>Home/Index</h2>
<p>@Model.Message</p>
If I run the program now, the ASP.NET MVC 3 system will show an error message saying “Object reference not set to an instance of an object,” pointing to this line of code in the Index.cshtml:
<p>@Model.Message</p>
To rectify the problem, I have to do items 2, 3, 4 and 5.
2. You create an instance of a CompositionContainer. You supply it with your marked code.
In Example 11, I declare a CompositionCotainer
and give it my HomeController
and MessageSource
class. I place this piece of code in the Application_Start
method in the global.ascx.cs file.
Example 11 — Declare CompositionContainer
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
CompositionContainer compositionContainer = new CompositionContainer();
compositionContainer.ComposeParts(new HomeController(),
new MessageSource());
}
MEF provides several ways of initializing a composition container. In Example 11, I’ve showed a simple and self-explanatory technique.
3. You implement the IDependencyResolver interface.
In Example 12, I add to my project a MefDependencySolver
class that implements the IDependencyResolver
interface. The IDependencyResolver
interface defines two methods that my class has to implement: GetService
and GetServices
.
Example 12 — Implementation of IDependencyResolver
namespace MefMvc01
{
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Web.Mvc;
public class MefDependencySolver : IDependencyResolver
{
public MefDependencySolver(CompositionContainer compositionContainer)
{
this.compositionContainer = compositionContainer;
}
private CompositionContainer compositionContainer;
public object GetService(Type serviceType)
{
string name = AttributedModelServices.GetContractName(serviceType);
return compositionContainer.GetExportedValueOrDefault<object>(name);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.compositionContainer
.GetExportedValues<object>(serviceType.FullName);
}
}
}
Later on, I will show how to register an instance of my MefDependencySolver
class with the ASP.NET MVC 3 system. When my application runs, the ASP.NET MVC 3 system knows of my MefDependencySolver
, and when a user requests a page, the ASP.NET MVC 3 system calls method GetService
several times, each time sending it a different “service.” Microsoft provides no documentation on “services” that the ASP.NET MVC 3 system sends into the GetService
method, but if you trace the execution of the application, you may observe that the ASP.NET MVC 3 system passes one or more of these service types in this order:
IControllerFactory
IControllerActivator
HomeController
ModelMetadataProvider
IViewPageActivator
Asp_Page_Views_Home_Index_cshtml
In the body of the GetService
method, I ask the MEF container to look for an object whose name matches the name of the service type. If the container finds such object, it returns it to the GetService
method, and the GetService
method sends it to the ASP.NET MVC 3 system. That way, the ASP.NET MVC 3 system knows that MEF takes responsibility for further processing. If the container doesn’t have the object, the method returns null
to the ASP.NET MVC 3 system. Hence, it is the ASP.NET MVC 3 system that is responsible for doing the work of the service.
Similarly, the ASP.NET MVC 3 system invokes the GetServices
method repeatedly, sending it one or more of these services in this order:
IFilterProvider
IModelBinderProvider
ValueProviderFactory
ModelValidatorProvider
IViewEngine
4. You supply the object that implements the IDependencyResolver interface with the instance of the CompositionContainer.
In Example 13, I pass the CompositionContainer
object to the constructor of my custom MefDependencySolver
. I place this piece of code in the global.asax.cs file.
Example 13 — Initializes my MefDependencySolver
with the CompositionContainer
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
CompositionContainer compositionContainer = new CompositionContainer();
compositionContainer.ComposeParts(new HomeController(),
new MessageSource());
var mefDependencySolver = new MefDependencySolver(compositionContainer);
}
5. You register your object that implements the IDependecyResolver with the ASP.NET MVC 3 system.
I have to inform the ASP.NET MVC 3 system of my custom dependency resolver. Hence, in Example 14, I register my MefDependencySolver
with the system, using the MVC 3 DependencyResolver
object.
Example 14 — Registers MefDependencySolver
with the ASP.NET MVC 3 system
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
CompositionContainer compositionContainer = new CompositionContainer();
compositionContainer.ComposeParts(new HomeController(),
new MessageSource());
var mefDependencySolver = new MefDependencySolver(compositionContainer);
DependencyResolver.SetResolver(mefDependencySolver);
}
Summary
This article shows how you use MEF in an ASp.NET MVC 3 application. You can accomplish most of your work with only three MEF constructs: ExportAttribute
, ImportAttribute
and CompositionContainer
. You have to do five things:
- You adorn the entities that a MEF composition container should match together with
ExportAttribute
or ImportAttribute
.
- You implement the
IDependencyResolver
interface.
- You create an instance of a
CompositionContainer
and initialize it with your entities.
- You initialize your tailor-made implementation of the
IDependencyResolver
interface with your composition container.
- You instruct the ASP.NET MVC 3
DependencyResolver
to use your implementation of the IDependencyResolver
interface.
MEF provides other capabilities that I have not discussed. I’ve intentionally kept examples very simple. Employing interfaces, abstract classes, bootstraps, etc. would merely obscure implementation requisites.
In the downloadable package, you’ll find two Visual Studio solutions. The ClassicMvc01
application is easy to understand. The MefMvc01
application does the same thing as ClassicMvc01
, but demonstrates essential coding measures you have to take if you want to utilize MEF in your ASP.NET MVC 3 applications.