ExportAttribute, ImportAttribute, CompositionContainer and MEF in Asp.Net MVC 3
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.
[ExportAttribute] public class A { public void ShowMessage() { Console.WriteLine("this is class A"); } }
public class B { [ImportAttribute] public A PropertyA { get; set; } }
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.
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
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.
namespace ClassicMvc01 { public class MessageSource { public MessageSource() { this.Message = "this message is from MessageSource"; } public string Message { get; private set; } } }
namespace ClassicMvc01.Controllers { using System.Web.Mvc; public class HomeController : Controller { private MessageSource messageSource = new MessageSource(); public ActionResult Index() { return View(this.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:
- 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 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.
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.
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.
@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.
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.
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.
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.
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.
Post Comment
CjtLTK I'm often to blogging and i really appreciate your content. The article has really peaks my interest. I'm going to bookmark your web site and hold checking for new information.
I3il3B I'm still learning from you, but I'm trying to achieve my goals. I absolutely liked reading all that is written on your blog.Keep the stories coming. I liked it!
lobQEW Wow! This can be one particular of the most beneficial blogs We've ever arrive across on this subject. Actually Great. I'm also a specialist in this topic therefore I can understand your hard work.
G0X75l I appreciate you sharing this blog post. Really Great.
ORxPPL Thanks a lot for the article.Really looking forward to read more. Great.
k78ggt Very good blog post.Much thanks again. Want more.
NDrFJ9 I really like and appreciate your blog article.Really looking forward to read more. Fantastic.
buy tramadol rx online - tramadol 717
legal order tramadol online - withdrawal symptoms of tramadol
buy tramadol 200mg online - buy cheap tramadol online cod
tramadol no prescription mastercard - tramadol dosage side effects
tramadol order online tramadol 50g - buy tramadol england
gradual withdrawal tramadol - tramadol hcl 50 mg tab street value
buy tramadol online saturday delivery - buy tramadol 100
cheap tramadol no prescription overnight - tramadol addiction in gaza
order tramadol online cheap - cheap tramadol next day
ZgcDKA I appreciate you sharing this blog.Really looking forward to read more. Really Great.
rxV2Pv Very informative article.Really thank you! Awesome.
SLinJJ Thanks a lot for the blog article.Really looking forward to read more. Want more.
Hit8fk Thank you ever so for you blog article.Really looking forward to read more. Much obliged.