Scott Hanselman

Extending NerdDinner: Adding MEF and plugins to ASP.NET MVC

May 20, '10 Comments [16] Posted in ASP.NET | ASP.NET MVC | NerdDinner | Open Source | Source Code
Sponsored By

The original NerdDinner Sample was very simple. Two samples, simple, in fact. Perhaps it's the new Northwind, as it's a good way to start working with ASP.NET MVC. However, it's not a perfect sample or an idealized example on how to do many things that users want to do.

Fortunately, there's been lots of cool folks in the community who have "forked" NerdDinner and done interesting stuff with it. Each of these samples is usually focused on a specific scenario, so they won't necessarily be merged with the trunk, but they are educational nonetheless.

Jon Galloway and I have also added a few things to NerdDinner, taking it in a more social direction, as Jon's MVC Music Store today is a better "getting started" sample for ASP.NET MVC 2. We'll be doing a series of posts on the interesting things the community has added to NerdDinner as well as some of the ones Jon and I added and presented at Mix a few months back. Soon Jon and I will release an updated NerdDinner v2 on CodePlex (although it's been in the source code tab for weeks) with lots of fixes, new features. We'll also add many of these "one off" samples as well and host them on CodePlex.

I spoke to Microsoft Engineer Hamilton Verissimo de Oliveira, aka "Hammett" (you likely know him from the Castle Project and Monorail) about making a NerdDinner sample that included MEF (Managed Extensibility Framework) since much of MEF is built into .NET 4 now. He was kind enough to do it, but I'm just blogging it now, so thanks to Hammett for his kindness and patience.

NerdDinner on MEF

MEF lives in System.ComponentModel.Composition. Hammett's done a number of interesting things it his sample, adding Microsoft.ComponentModel.Composition.Extensions and Microsoft.ComponentModel.Composition.Extensions.Web namespaces building in some nice extension methods for common techniques as well as and implementation of IControllerFactory and a derivation of HttpApplication.

MefControllerFactory

Remember MEF is about making applications easily composable. In this sample Hammett has created his own MefControllerFactory, replacing the default controller factory that comes with ASP.NET MVC. ASP.NET MVC makes it easy to change:

protected override void Application_Start()
{
base.Application_Start();

ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(base.ScopeManager));

RegisterRoutes(RouteTable.Routes);

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MobileCapableWebFormViewEngine());
}

Notice his controller factory has a ScopeManager. This is a web application, and some components might be at Application Scope (create them once and hang on) and some might be at Request scope (make a new one each request).

For controllers, he's effectively recreated the default behavior of the ASP.NET MVC's controller factory, but in doing it, has given us lots of ways we can jump in an change the behavior in exotic ways by overriding CreateRootCatalog in MefhttpApplication. It's default implementation looks in /bin:

protected virtual ComposablePartCatalog CreateRootCatalog()
{
return new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
}

As you probably know, ASP.NET MVC looks for classes via a convention. It looks for classes with the word "Controller" at the end that are also IController implmentations. Here's the MEF way to declare that convention using Microsoft.ComponentModel.Composition.Extensions. Note the use of scope.

[assembly: Discovery(typeof(Conventions))]

namespace NerdDinner
{
public class Conventions : ConventionDiscovery
{
public Conventions()
{
Add(PartRegistration.
Types(t => t.Name.EndsWith("Controller") && !t.IsAbstract).
Exporting((c, t) => c.
Contract(typeof (IController)).
Metadata("Name", t.Name.Substring(0, t.Name.Length - "controller".Length)).
Metadata("Mode", WebScopeMode.Request))
);
}
}
}

Pretty cool.

Controllers and their Needs

Whenever a more advanced programmer looks as the NerdDinner code they usually say they they really don't like this:

public DinnersController() : this(new DinnerRepository()) 
{
}

public DinnersController(IDinnerRepository repository)
{
dinnerRepository = repository;
}

The second constructor takes an IDinnerRepository, allowing us to make different implementations, but the default constructor says, "well, here's a concrete implementation if you don't give one." It's a slippery slope and by adding the default implementation I get to sidestep using dependency injection while making the controller testable, but I've tied my controller down with a direct dependency to the DinnerRepository. This is sometimes called "Poor Man's IoC" and many would say that this is a very poor man. That's a religious argument, but Hammett takes a stand by removing the default constructor.

public class DinnersController : Controller
{
private IDinnerRepository dinnerRepository;

public DinnersController(IDinnerRepository repository)
{
dinnerRepository = repository;
}
//...
}

So how does a DinnersController ever get an IDinnerRepository? The idea is that it's not the controllers job to worry about the how, it's only its job to want.

MEF is effectively a Dating Service for Components. Here DinnerRepository is saying it's available and it wants to meet someone who is also into "IDinnerRepository."

[Export(typeof(IDinnerRepository))]
public class DinnerRepository : NerdDinner.Models.IDinnerRepository {

That [Export] attribute is its way to saying, "I'm on the market. Matchmaker, make a match!" When MEF is asked for a Controller and it notices that it has no default constructor, as in our case, it looks at the available constructors and says, "Oh, DinnersController wants to meet someone too! I I think I know just your type." Then it creates a DinnerRepository and calls the DinnersController constructor passing it in. It injects the dependency.

Often you'll see the other components advertising their interest with an [Import] attribute, but that's not necessary in this example because all the Controllers were created via the MefControllerFactory. They don't need attributes, as they've already walked in the door of our dating service!

Other Services MEFified

Recently in a review of MVC Music Store Ayende (and others before him, as well) dissed on these line of code, which actually come with ASP.NET MVC 2 out of the box and weren't written for the sample. (Although they weren't changed)  Phil can speak to specific decisions as I wasn't involved, but many folks who are into dependency injection don't like this. This is effectively the same maneuver as shown above, written slightly differently.

public class AccountController : Controller 
{
public AccountController()
: this(null, null) {
}

public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
{
FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService();
}
//...
}

Hammett's implementation just uses MEF, so:

public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
{
FormsAuth = formsAuth;
MembershipService = service;
}

Again, the whole point is that the dependencies get figured out automatically, each one wearing the "I'm available for hooks ups" attributes of [Export(typeof(IFormsAuthentication))] and [Export(typeof(IMembershipService))] respectively.

All in all, MEF is a nice clean addition to an ASP.NET MVC app. Thanks to Hammett for his hard work!

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, May 20, 2010 8:42:57 AM UTC
Scary how much a like this looks to the convention model I've built for MEF Contrib


:-)
Thursday, May 20, 2010 8:52:59 AM UTC
Interesting, but it solves only part of the "MVC with plugins" puzzle. I'd rather have each plugin into it's own directory. Right now you need to scatter the plugin stuff over /bin, /views and /content. I think it's a subpar solution (no easy removing or upgrading of plugins, for example). I've been trying to get this to work (having plugins contained in their own folder) but it's a bitch. Or: I'm doing it wrong. :)
Thursday, May 20, 2010 8:57:03 AM UTC
I just realized that I know nothing about MEF :-/ ohh boy... back to the reading material!

Thank you for the blog post Scott.
Thursday, May 20, 2010 9:21:50 AM UTC
The actual issue with MEF, the last time I checked was managing service lifetime as PerWebRequest, without it, it is difficult to implement _UnitOfWork_ like pattern. Though child container can be created for each request, but there is bit of hack involved. This is the reason I think mef was never designed for _web_ application.
Thursday, May 20, 2010 9:50:22 AM UTC
What happens you have more than one repository decorated with [Export(typeof(IDinnerRepository))]?

Is there a registry class (ala StructureMap) to create application wide relationships?

Rich
Thursday, May 20, 2010 10:38:36 AM UTC
>>> Two samples, simple, in fact. Perhaps it's the new Northwind, as it's a good way to start working with ASP.NET MVC. However, it's not a perfect sample or an idealized example on how to do many things that users want to do.

Are you intentionally sending a signal to ayende not to code review NerdDinner?
dgchamp01
Thursday, May 20, 2010 12:09:06 PM UTC
I already have DI, what I want to do is use MEF to provide custom functionality to my clients. At the moment we have a custom view engine that uses the JS, CSS, Views and Master Pages associated with a client and Ninject for DI. At this stage we are planning on using Ninject modules to inject concrete classes based on the user. Still not a plugin but we have not been able to work out how we can use MEF to do this any better.
abarr
Thursday, May 20, 2010 12:11:31 PM UTC
dgchamp01 - Ha! No, I'm just reminding beginners that this is a Sample, not a "complete company in a box." It's not the gospel by any means.

Inferis, can't you override this and put your plugins where ever?


protected virtual ComposablePartCatalog CreateRootCatalog()
{
return new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
}
Thursday, May 20, 2010 1:14:03 PM UTC
Very interesting Scott. Just a reminder that MEF is a decent IOC as well, not just a plugin infrastructure. I think you guys should sell more on MEF being a Dependency Injection/Inversion of Control. Until very recently I wasn't aware that MEF is an alternative to StructureMap, Ninject, AutoFAC etc. Being built into .NET, if I am doing .NET 4 and I am not running those edge cases, why would I ever use anything other then MEF?
Thursday, May 20, 2010 1:47:53 PM UTC
Hm, According to your article, MEF seems to be a simple Dependency Injection framework, while it is so much more than that... I am doing just the same using structure map.
Will you blog about the bigger advantage that MEF could bring? Like a **really** composable Application where dropping a dll into a plugin folder registered by MEF would add a new menu to the Web App for example?
Thursday, May 20, 2010 1:51:31 PM UTC
@scott: sure, but that only finds the MEF components. What about your .aspx files for the views and their related content (images, css, ...)?

The easy solution is putting it all in the standard folders, but that gets messy. I'd like xcopy plugin deployment where I just unpack a zipfile containing a plugin into a known folder and that's it. As far as I found out: that's hard to get right.
Thursday, May 20, 2010 8:08:17 PM UTC
MEF seems to be a strange fit for an IoC container, where typically there are many 1 to 1 mappings (interface to a concrete class) and a registry (missing from MEF) to provide the specific configuration (staging/production). MEF seems to be a better fit where there can be multiple implementations which are all used. If this is a dating service it is one for meet ups where there are more than 2 participants.
Sunday, May 23, 2010 10:10:47 AM UTC
I am curious about how would you build a whole module for an ASP.NET MVC app. I mean, not just the controllers, but views and models too. Data access too.
Tuesday, June 01, 2010 7:55:24 AM UTC
Interesting implementation. I like the DI aspects which I haven't really captured in mine yet, well not my blogged version, will get an blog update out soon. Comparing and contrasting to my version (@http://www.fidelitydesign.net/?p=104), what would you recommend for improvements?
Tuesday, June 01, 2010 8:11:57 AM UTC
I'm a little supprised to see MEF being used as a IoC container - didn't the MEF team try to distinct MEF from Unity? I thought it didn't really want to be just another IoC but rather a framework for a plugin infrastructure. And I wouldn't see repository-implementations as being something I would use plugins for.
Sunday, September 05, 2010 2:00:29 AM UTC
Hi guy's
For those of us who are not C# programmers, could you provde us wth some Vsual Basic examples for the MobileCapableWebFormViewEngine code that you use to access the MDBF file

Thanks in advance

Scott
Scott
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.