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 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.

To run code samples in this article, you should have:

  • 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 following pieces of code:

  • class
  • field
  • property
  • indexer
  • method.

You use an ImportAttribute to mark 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 of 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)
    {
      // Declare a composition container.
      CompositionContainer compositionContainer = new CompositionContainer();

      // Feed the container instances of A and B.
      compositionContainer.ComposeParts(new A(), new B());

      // Retrieve the composed part.
      B b = compositionContainer.GetExportedValueOrDefault<B>();

      // Use the imported construct of 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 Picture 1. You’re an experienced developer and you can guess the structure of the application in Picture 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

Picture 1 — Asp.Net MVC 3 application. Text within a red border originates in a separate class.

picture-1.png

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

  1. You mark pieces of code that MEF should take care of with ExportAttributes and ImportAttributes.
  2. You create an instance of a CompositionContainer. You supply it with your marked code.
  3. You implement the IDependencyResolver interface.
  4. You supply the object that implements the IDependencyResolver interface with the instance of the CompositionContainer.
  5. You register your object that implements the IDependecyResolver 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.Compostion 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.asax.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.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"