I want to use MEF with asp.net mvc. I wrote following controller factory:
public class MefControllerFactory : DefaultControllerFactory
{
private CompositionContainer _Container;
public MefControllerFactory(Assembly assembly)
{
_Container = new CompositionContainer(new AssemblyCatalog(assembly));
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
{
var controllers = _Container.GetExports<IController>();
var controllerExport = controllers.Where(x => x.Value.GetType() == controllerType).FirstOrDefault();
if (controllerExport == null)
{
return base.GetControllerInstance(requestContext, controllerType);
}
return controllerExport.Value;
}
else
{
throw new HttpException((Int32)HttpStatusCode.NotFound,
String.Format(
"The controller for path '{0}' could not be found or it does not implement IController.",
requestContext.HttpContext.Request.Path
)
);
}
}
}
In Global.asax.cs I'm setting my controller factory:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory.MefControllerFactory(Assembly.GetExecutingAssembly()));
}
I have an area:
[Export(typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller
{
private readonly IArticleService _articleService;
[ImportingConstructor]
public HomeController(IArticleService articleService)
{
_articleService = articleService;
}
//
// GET: /Articles/Home/
public ActionResult Index()
{
Article article = _articleService.GetById(55);
return View(article);
}
}
IArticleService
is an interface.
There is a class which implements IArticleService
and Exports it.
It works.
Is this everything what I need for working with MEF?
How can I skip setting PartCreationPolicy
and ImportingConstructor
for controller?
I want to set my dependencies using constructor.
When PartCreationPolicy
is missing, I get following exception:
A single instance of controller 'MvcApplication4.Areas.Articles.Controllers.HomeController' cannot be used to handle multiple requests. If a custom controller factory is in use, make sure that it creates a new instance of the controller for each request.
The Managed Extensibility Framework or MEF is a library for creating lightweight, and extensible applications. It allows application developers to discover and use extensions with no configuration required. It also lets extension developers easily encapsulate code and avoid fragile hard dependencies.
The primary difference between ASP.NET MVC and ASP.NET Core is their cross-platform approach. ASP.NET Core can be used on Windows, Mac, or Linux, whereas ASP.NET MVC can only be used for applications on Windows.
For those who don't know, the Managed Extensibility Framework (MEF) is alive and well, and has been ported to . NET Core as System. Composition (source here).
Your technique here is pretty solid, and will work even in partial trust. The nerd dinner MEF example has extensions that will allow you to deal with discovering controllers by convention and making them into MEF exports automatically, without flagging them with MEF attributes. But managing part catalogs directly doesn't work in partial trust, so the nerd dinner MEF techniques don't work in partial trust.
If you are working in full trust, and want convention based discovery with your controllers start with the Nerd Dinner MEF example, but you should probably also read about a couple major issues with the nerd dinner MEF example that will come up if your own application's model is in a separate class a library project. I blogged about those cases and suggested some fixes.
If you aren't all that interested in the convention based discovery stuff, then nerd dinner sample is a tad over-engineered. Your solution is probably fine as is... and works in partial trust too, which is always a bonus.
[update] I did spot one potential problem with your technique:
var controllerExport = controllers.Where(x => x.Value.GetType() ==
controllerType).FirstOrDefault();
In the where clause here, you are calling .Value on each export in the collection of parts... that is actually going to cause each of those exports to be composed and instantiated in order to be evaluated. That could be a nasty performance issue.
You might consider decorating your controllers with named export contracts like this:
[Export("Home", typeof(IController))]
Then using a controler factory like this instead:
public class MefControllerFactory: IControllerFactory
{
private CompositionContainer _Container;
public MefControllerFactory(Assembly assembly)
{
_Container = new CompositionContainer(new AssemblyCatalog(assembly));
}
#region IControllerFactory Members
public IController CreateController(RequestContext requestContext, string controllerName)
{
var controller = _Container.GetExportedValue<IController>(controllerName);
if (controller == null)
{
throw new HttpException(404, "Not found");
}
return controller;
}
public void ReleaseController(IController controller)
{
// nothing to do
}
#endregion
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With