Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Azure, PHP, OpenXML, VSTS, ...

About the author

Maarten Balliauw is currently employed as .NET Technical Consultant at RealDolmen. His interests are mainly web applications developed in ASP.NET (C#) or PHP and the Windows Azure cloud platform.
More about me More about me
Send mail E-mail me


ASP.NET MVC Quickly Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
View Maarten Balliauw's MVP profile

Search

Latest Twitter

    Follow me on Twitter...

    My projects

    Disclaimer

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

    © Copyright Maarten Balliauw 2010

    ASP.NET MVC and the Managed Extensibility Framework (MEF)

    Microsoft’s Managed Extensibility Framework (MEF) is a .NET library (released on CodePlex) that enables greater re-use of application components. You can do this by dynamically composing your application based on a set of classes and methods that can be combined at runtime. Think of it like building an appliation that can host plugins, which in turn can also be composed of different plugins. Since examples say a thousand times more than text, let’s go ahead with a sample leveraging MEF in an ASP.NET MVC web application.

    kick it on DotNetKicks.com

    Getting started…

    The Managed Extensibility Framework can be downloaded from the CodePlex website. In the download, you’ll find the full source code, binaries and some examples demonstrating different use cases for MEF.

    Now here’s what we are going to build: an ASP.NET MVC application consisting of typical components (model, view, controller), containing a folder “Plugins” in which you can dynamically add more models, views and controllers using MEF. Schematically:

    Sample Application Architecture

    Creating a first plugin

    Before we build our host application, let’s first create a plugin. Create a new class library in Visual Studio, add a reference to the MEF assembly (System.ComponentModel.Composition.dll) and to System.Web.Mvc and System.Web.Abstractions. Next, create the following project structure:

    Sample Plugin Project

    That is right: a DemoController and a Views folder containing a Demo folder containing Index.aspx view. Looks a bit like a regular ASP.NET MVC application, no? Anyway, the DemoController class looks like this:

    [Export(typeof(IController))]
    [ExportMetadata("controllerName", "Demo")]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class DemoController : Controller
    {
        public ActionResult Index()
        {
            return View("~/Plugins/Views/Demo/Index.aspx");
        }
    }

    Nothing special, except… what are those three attributes doing there, Export and PartCreationPolicy? In short:

    • Export tells the MEF framework that our DemoController class implements the IController contract and can be used when the host application is requesting an IController implementation.
    • ExportMetaData provides some metadata to the MEF, which can be used to query plugins afterwards.
    • PartCreationPolicy tells the MEF framework that it should always create a new instance of DemoController whenever we require this type of controller. By defaukt, a single instance would be shared across the application which is not what we want here. CreationPolicy.NonShared tells MEF to create a new instance every time.

    Now we are ready to go to our host application, in which this plugin will be hosted.

    Creating our host application

    The ASP.NET MVC application hosting these plugin controllers is a regular ASP.NET MVC application, in which we’ll add a reference to the MEF assembly (System.ComponentModel.Composition.dll). Next, edit the Global.asax.cs file and add the following code in Application_Start:

    ControllerBuilder.Current.SetControllerFactory(
        new MefControllerFactory(
            Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins")));

    What we are doing here is telling the ASP.NET MVC framework to create controller instances by using the MefControllerFactory instead of ASP.NET MVC’s default DefaultControllerFactory. Remember that everyone’s always telling ASP.NET MVC is very extensible, and it is: we are now changing a core component of ASP.NET MVC to use our custom MefControllerFactory class. We’re also telling our own MefControllerFactory class to check the “Plugins” folder in our web application for new plugins. By the way, here’s the code for the MefControllerFactory:

    public class MefControllerFactory : IControllerFactory
    {
        private string pluginPath;
        private DirectoryCatalog catalog;
        private CompositionContainer container;

        private DefaultControllerFactory defaultControllerFactory;

        public MefControllerFactory(string pluginPath)
        {
            this.pluginPath = pluginPath;
            this.catalog = new DirectoryCatalog(pluginPath);
            this.container = new CompositionContainer(catalog);

            this.defaultControllerFactory = new DefaultControllerFactory();
        }

        #region IControllerFactory Members

        public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            IController controller = null;

            if (controllerName != null)
            {
                string controllerClassName = controllerName + "Controller";
                Export<IController> export = this.container.GetExports<IController>()
                                                 .Where(c => c.Metadata.ContainsKey("controllerName")
                                                     && c.Metadata["controllerName"].ToString() == controllerName)
                                                 .FirstOrDefault();
                if (export != null) {
                    controller = export.GetExportedObject();
                }
            }

            if (controller == null)
            {
                return this.defaultControllerFactory.CreateController(requestContext, controllerName);
            }

            return controller;
        }

        public void ReleaseController(IController controller)
        {
            IDisposable disposable = controller as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }

        #endregion
    }

    Too much? Time for a breakdown. Let’s start with the constructor:

    public MefControllerFactory(string pluginPath)
    {
        this.pluginPath = pluginPath;
        this.catalog = new DirectoryCatalog(pluginPath);
        this.container = new CompositionContainer(catalog);

        this.defaultControllerFactory = new DefaultControllerFactory();
    }

    In the constructor, we are storing the path where plugins can be found (the “Plugins” folder in our web application). Next, we are telling MEF to create a catalog of plugins based on what it can find in that folder using the DirectoryCatalog class. Afterwards, a CompositionContainer is created which will be responsible for matching plugins in our application.

    Next, the CreateController method we need to implement for IControllerFactory:

    public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        IController controller = null;

        if (controllerName != null)
        {
            string controllerClassName = controllerName + "Controller"
            Export<IController> export = this.container.GetExports<IController>()
                                             .Where(c => c.Metadata.ContainsKey("controllerName"
                                                 && c.Metadata["controllerName"].ToString() == controllerName)
                                             .FirstOrDefault();
            if (export != null) {
                controller = export.GetExportedObject();
            }
        }

        if (controller == null)
        {
            return this.defaultControllerFactory.CreateController(requestContext, controllerName);
        }

        return controller;
    }

    This method handles the creation of a controller, based on the current request context and the controller name that is required. What we are doing here is checking MEF’s container for all “Exports” (plugins as you wish) that match the controller name. If one is found, we return that one. If not, we’re falling back to ASP.NET MVC’s DefaultControllerBuilder.

    The ReleaseController method is not really exciting: it's used by ASP.NET MVC to correctly dispose a controller after use.

    Running the sample

    First of all, the sample code can be downloaded here: MvcMefDemo.zip (270.82 kb)

    When launching the application, you’ll notice nothing funny. That is, untill you want to navigate to the http://localhost:xxxx/Demo URL: there is no DemoController to handle that request! Now compile the plugin we’ve just created (in the MvcMefDemo.Plugins.Sample project) and copy the contents from the \bin\Debug folder to the \Plugins folder of our host application. Now, when the application restarts (for example by modifying web.config), the plugin will be picked up and the http://localhost:xxxx/Demo URL will render the contents from our DemoController plugin:

    Sample run MEF ASP.NET MVC

    Conclusion

    The MEF (Managed Extensibility Framework) offers a rich manner to dynamically composing applications. Not only does it allow you to create a plugin based on a class, it also allows exporting methods and even properties as a plugin (see the samples in the CodePlex download).

    By the way, sample code can be downloaded here: MvcMefDemo.zip (270.82 kb)

    kick it on DotNetKicks.com


    Categories: ASP.NET | C# | General | MEF | MVC | Quality code

    Comments

    DotNetKicks.com | Reply

    Tuesday, April 21, 2009 3:40 PM

    trackback

    ASP.NET MVC and the Managed Extensibility Framework (MEF)

    You've been kicked (a good thing) - Trackback from DotNetKicks.com

    DotNetShoutout | Reply

    Tuesday, April 21, 2009 3:47 PM

    trackback

    ASP.NET MVC and the Managed Extensibility Framework (MEF)

    Thank you for submitting this cool story - Trackback from DotNetShoutout

    Mark Perry United Kingdom | Reply

    Tuesday, April 21, 2009 4:11 PM

    Mark Perry

    Great tutorial. I was a couple of days out of putting together something like this myself.

    Keep up the good work.

    Mark

    runxc United States | Reply

    Tuesday, April 21, 2009 4:31 PM

    runxc

    MEF looks very promising.  It seems that extensibility is something that we are all trying to achieve.  I love the article but find myself wanting more...  How does this work in say a standard ASP.Net environment or say you have a web site using Web parts.   How could you add or remove these web parts or widgets dynamically.  

    Maarten Balliauw Belgium | Reply

    Tuesday, April 21, 2009 10:20 PM

    Maarten Balliauw

    Are you saying to, for example, let MEF fill up your web part catalog? If so, let me know and I'l lcheck if I can do a post on something like that.

    huey | Reply

    Tuesday, April 21, 2009 4:34 PM

    huey

    So you have to give explicit views due to it being in the plugins directory yes?  Although that is pretty simple to customize too.

    hammett Canada | Reply

    Tuesday, April 21, 2009 8:13 PM

    hammett

    Hi, awesome work! Thanks for sharing it. Just a note: your implementation of ReleaseController should call container.ReleaseExport( controllerExport ) instead. This way MEF can release references and dispose any nonshared/disposable part in the graph that the controller started.

    I will publish a sample on MEF/MVC this week that shows how to do that. Stay tuned Smile

    Thanks

    Maarten Balliauw Belgium | Reply

    Tuesday, April 21, 2009 10:35 PM

    Maarten Balliauw

    Let me know the link to your post? I'll update my code to reflect your tips. Thanks!

    hammett Canada | Reply

    Tuesday, April 21, 2009 8:32 PM

    hammett

    Also, c => c.GetExportedObject() is very bad Smile  
    You're constructing all controllers (and their dependency chain). The best approach would be to associate a metadata with the controller, or use a custom catalog for controllers.

    Maarten Balliauw Belgium | Reply

    Tuesday, April 21, 2009 10:36 PM

    Maarten Balliauw

    Agree with that... THought the non-metadata approach would be clearer for a blog post, but then again... metadata is better. Thanks for the tip!

    Daniel Earwicker United Kingdom | Reply

    Thursday, May 14, 2009 10:43 AM

    Daniel Earwicker

    Not necessarily bad - depends where you put it in the chain of linq operators. If you put it after the Where but before the FirstOrDefault, you could assign the result of the expression directly to 'controller', and miss out the test for export != null (lose the export variable altogether). This would be equivalent to your current code but all in a single expression, and would only construct one controller, not all of them.

    Jeff United States | Reply

    Tuesday, April 21, 2009 11:44 PM

    Jeff

    very cool, I like it alot.  I have been working on somthing like this,  it helps alot.  Is it possible to say be able to update these "plug-ins" on the fly.  So from my app a can download a new version from some sort of online repository and have it overwrit the one in the plugin directory.  It was always a problem with like assembly.load and what not.  but i am not sure if mef resolves this.

    maartenba Belgium | Reply

    Wednesday, April 22, 2009 8:11 AM

    maartenba

    That should work, you'll have to issue a catalog.Refresh() in code though. Also, I've noticed the ASP.NET devserver locks the plugin dll's which makes it a bit difficult to overwrite while running. But in theory, it should work.

    Jeff United States | Reply

    Friday, April 24, 2009 6:17 PM

    Jeff

    catalog.refresh() I can handle it.  The locks is what i was really wondering about.  Any tips, before I just dive in and start hitting walls?

    ringo Italy | Reply

    Wednesday, April 22, 2009 11:43 AM

    ringo

    Great article!
    What about including the views as a embedded resource in the plugin dll and then create a route to find them?
    What's the impact on performances?

    maartenba Belgium | Reply

    Wednesday, April 22, 2009 11:47 AM

    maartenba

    That is another option, true. Not sure about performance though.

    DotNetBurner - ASP.net MVC | Reply

    Wednesday, April 22, 2009 1:40 PM

    trackback

    ASP.NET MVC and the Managed Extensibility Framework (MEF)

    DotNetBurner - burning hot .net content

    progg.ru | Reply

    Thursday, April 23, 2009 6:13 PM

    trackback

    ASP.NET MVC и Managed Extensibility Framework (MEF)

    Thank you for submitting this cool story - Trackback from progg.ru

    hammett Canada | Reply

    Thursday, April 23, 2009 8:03 PM

    hammett

    Sure, here it goes
    blogs.msdn.com/.../mef-and-asp-net-mvc-sample.aspx

    maartenba Belgium | Reply

    Friday, April 24, 2009 7:32 AM

    maartenba

    Great post!

    Jack United States | Reply

    Saturday, April 25, 2009 3:24 AM

    Jack

    Nice article, 'KICK' it!

    maartenba Belgium | Reply

    Monday, April 27, 2009 3:37 PM

    maartenba

    Another tip: you can use this in conjunction with www.wynia.org/.../ to embed your views as an embedded resource.

    Felix Italy | Reply

    Tuesday, June 16, 2009 2:28 PM

    Felix

    Great Post!
    Just a question:
    How about customize the navigation Menu to add new link for each Controller?

    maartenba Netherlands | Reply

    Wednesday, June 17, 2009 10:25 AM

    maartenba

    You can use http://mvcsitemap.codeplex.com for that and dynamically add MvcSiteMapNodes. Or create your own navigation handler which also queries MEF for certain exports.

    Servefault.com | Reply

    Wednesday, October 28, 2009 11:15 AM

    trackback

    ASP.NET MVC and the Managed Extensibility Framework (MEF)

    Thank you for submitting this cool story - Trackback from Servefault.com

    Tom United States | Reply

    Thursday, November 19, 2009 7:14 AM

    Tom

    Hi.  Thank you for your article and introduction to MEF for MVC.  I noticed the views in your plugin are not strongly typed.  I tried modifying your code to make your demo Index.aspx view strongly typed and it failed.  Can you make views in the plugins strongly typed as opposed to passing data to the view via ViewModel dictionary?  Thanks

    maartenba Belgium | Reply

    Thursday, November 19, 2009 9:41 PM

    maartenba

    Check blog.maartenballiauw.be/.../...ramework-(MEF).aspx - it's not the prettiest workaround but It will make strongly typed views work.

    Tom United States | Reply

    Thursday, November 19, 2009 10:55 PM

    Tom

    Thank you for the reply.  I downloaded your example and it works.  However, I noticed your view code does not actually display any data from the strongly typed model.  I went to augment the view to display the DemoProperty element from  MvcMefDemo.Plugins.Sample.Models.DemoModel.  I was expecting to do this:

    <%= Model.DemoProperty %>

    but Model is not an option in Intellisense.  I then put the code back like it was, recompiled, then copied new Views folder and generated DLLs from MvcMefDemo.Plugins.Sample/bin/debug to MvcMefDemo.Web/Plugins.  I reloaded the Demo page in the browser and got :

    Could not load 'System.Web.Mvc.ViewPage<MvcMefDemo.Plugins.Sample.Models.DemoModel>

    So, I am not sure how to proceed.  Any ideas?  Thanks

    Tom

    Rob United Kingdom | Reply

    Thursday, May 13, 2010 8:30 PM

    Rob

    Could this be done using a similar method to:

    www.hanselman.com/.../CommentView.aspx

    Maarten Balliauw {blog} | Reply

    Tuesday, July 27, 2010 2:27 PM

    trackback

    Revised: ASP.NET MVC and the Managed Extensibility Framework (MEF)

    Revised: ASP.NET MVC and the Managed Extensibility Framework (MEF)

    Add comment




      Country flag

    biuquote
    • Comment
    • Preview
    Loading