Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MEF and ASP.NET MVC

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.

like image 517
denis_n Avatar asked Jun 13 '10 20:06

denis_n


People also ask

What is MEF used for?

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.

What is difference between MVC and ASP.NET MVC?

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.

Is MEF supported in .NET core?

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


1 Answers

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
}
like image 154
Stephen M. Redd Avatar answered Sep 28 '22 09:09

Stephen M. Redd