Ok, I have a solution now.
The MEFcontrib offers good support for the convention based model. So simply NuGet it(install-package mefcontrib) to your mvc 4 project.
Once you have the mefcontrib binaries, you just need to register the conventions first and then harness it by adding the convention catalogue. Below snippet shows how:
Convention registration:
public class InitPartsConvention : PartRegistry
{
public InitPartsConvention()
{
Scan(x => x.Assembly(Assembly.GetExecutingAssembly()));
Part()
.ForTypesAssignableFrom<IHttpController>()
.MakeNonShared()
.Export()
.Imports(x =>
{
x.Import().Members(
m => new[] {
m.GetConstructors()
.FirstOrDefault(
c => c.GetCustomAttributes(typeof(ImportingConstructorAttribute), false).Length > 0)
?? m.GetGreediestConstructor()
});
x.Import().Members(
m => m.GetMembers(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic)
.Where(
mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray()
);
});
Part()
.ForTypesAssignableFrom<IController>()
.MakeNonShared()
.Export()
.Imports(x =>
{
x.Import().Members(
m => new[] {
m.GetConstructors()
.FirstOrDefault(
c => c.GetCustomAttributes(typeof(ImportingConstructorAttribute), false).Length > 0)
?? m.GetGreediestConstructor()
});
x.Import().Members(
m => m.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(
mbr => mbr.GetCustomAttributes(typeof(ImportAttribute), false).Length > 0).ToArray()
);
});
}
}
This registers your api controllers and mvc controller so that they can be MEFified. It does not cover the AsyncController however.
MEF MVC bootstrapper
public static class MefBootstrapper
{
public static void RegisterMef()
{
var container = GetContainer();
var resolver = new MefDependencyResolver(container);
// Install MEF dependency resolver for MVC
DependencyResolver.SetResolver(resolver);
// Install MEF dependency resolver for Web API
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver
= resolver;
}
private static CompositionContainer GetContainer()
{
var path = HostingEnvironment.MapPath("~/bin");
if (path == null) throw new Exception("Unable to find the path");
var catelog = new AggregateCatalog(
new DirectoryCatalog(path),
new ConventionCatalog(new InitPartsConvention())); // this adds the convention to MEF
return new CompositionContainer(catelog);
}
}
That's it, job done! Enjoy dynamic DI in your MVC 4 apps.