Jun
01
2010

Pluggable MVC 2.0 using MEF and strongly-typed views

The problem…

I’ve been putting together a pluggable MVC framework using MEF in C# 4.0.

MEF was chosen over other solutions such as Portable Areas as it allows all plugins to be composed together as a single site at runtime without the assemblies needing to reference each other.

After scouring the web for ideas, I stumbled upon Maarten Balliauw’s excellent posts for accomplishing this task:

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

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

It seems that there are issues using strongly-typed views in this scenario, though, as the types referenced in the views are not found or loaded at runtime. Maarten’s solution is to copy the plugins into the main website’s bin directory so that they can be discovered at runtime.

The solution…

This can also be solved in another (more elegant?) way by using AssemblyResolve to tell the framework where to load a type from when it is requested by the views.
In this way, your plugin directory needn’t be a sub-directory of the website’s bin folder.

Unfortunately, this doesn’t work without a bit of fettling and the key to getting it working is to give the framework a hint to the assembly the type is in. This in turn forces the AssemblyResolve event to fire. To accomplish this, use an assembly directive at the top of your strongly-typed views as follows:

<%@ Assembly Name="Web.Plugins.Controls" %>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Web.Plugins.Controls.Models.MenuModel>" %>

An example of using AssemblyResolve by David Morton can be found here:

Assembly Resolution with AppDomain.AssemblyResolve

Which can be bent to our will:

public void Initialize()
{
	// Add assembly handler for strongly-typed view models
	AppDomain.CurrentDomain.AssemblyResolve += PluginAssemblyResolve;
}

private Assembly PluginAssemblyResolve(object sender, ResolveEventArgs resolveArgs)
{
	Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();

	// Check we don't already have the assembly loaded
	foreach (Assembly assembly in currentAssemblies)
	{
		if (assembly.FullName == resolveArgs.Name || assembly.GetName().Name == resolveArgs.Name)
		{
			return assembly;
		}
	}

	// Load from directory
	return LoadAssemblyFromPath(resolveArgs.Name, _fullPluginPath);
}

private static Assembly LoadAssemblyFromPath(string assemblyName, string directoryPath)
{
	foreach (string file in Directory.GetFiles(directoryPath))
	{
		Assembly assembly;

		if (TryLoadAssemblyFromFile(file, assemblyName, out assembly))
		{
			return assembly;
		}
	}

	return null;
}

private static bool TryLoadAssemblyFromFile(string file, string assemblyName, out Assembly assembly)
{
	try
	{
		// Convert the filename into an absolute file name for
		// use with LoadFile.
		file = new FileInfo(file).FullName;

		if (AssemblyName.GetAssemblyName(file).Name == assemblyName)
		{
			assembly = Assembly.LoadFile(file);
			return true;
		}
	}
	catch
	{
	}

	assembly = null;
	return false;
}

The download…

http://www.thegecko.org/uploads/MefMvc.zip

This is an example framework based on Maarten’s using this method. It requires C# 4.0 and Visual Studio 2010 and it is recommended to run without debugging to avoid slow execution when view caching is disabled (Don’t Panic).

The sample framework includes the following functionality:

  • Embedded resources handling
  • Plugin registration priority for selecting controllers and views
  • Menu and menu item registration with ordering
  • Sample authentication module
  • Automatic site.master resolving

The resource handling is borrowed from the Spark view engine sample modules:

Spark modules

This should serve as a nice starting point for your next MVC pluggable project :)

Written by Rob in: Development | Tags: , , , ,

19 Comments »

  • [...] This post was mentioned on Twitter by Doga Oztuzun, thegecko. thegecko said: Pluggable #ASPNet #MVC using #MEF and strongly-typed views http://bit.ly/akizra [...]

    Pingback | June 2, 2010
  • [...] to Vote[Del.icio.us] techBox » Pluggable MVC 2.0 using MEF and strongly-typed views (6/2/2010)Wednesday, June 02, 2010 from [...]

    Pingback | June 2, 2010
  • Terry Aney

    I’d like to CC/BCC you on an email about this article/framework that I am in discussions with some experts from MS. Would you like to be included? If so, please email me.

    Comment | July 8, 2010
  • Alex

    Hello Rob,
    Awesome work! Thanks for sharing it. I found some commented code for registering the area’s. But couldn’t figure it out how to accomplish that. I’d like to register each plugin as an area. Is it possible with your solution?
    Thanks,
    Alex

    Comment | August 20, 2010
  • Rob

    Hi Alex, I did start looking at that, but the project it was for didn’t need it. It should be fairly easy to accomplish with something similar to the commented out code.
    I’d start by looking at the ASP.Net MVC 2 source code to see how area routes are registered. Cheers, Rob

    Comment | August 26, 2010
  • leon

    Awesome work! Maarten’s revised sample, upon which your framework seems to be based, uses MVC 3 whereas your’s reverts to MVC 2. What would be needed by way of tweaks to your framework to fit it into MVC 3 ?

    Comment | August 30, 2010
  • Rob

    Hi Leon, I haven’t had a chance to look at MVC 3.0 yet, so I honestly don’t know :)

    Comment | September 1, 2010
  • Misha

    CSS and images?

    Comment | September 30, 2010
  • Mikhal

    Thanks for a wonderful project! Please help. How to add embedded the resource files?

    Comment | October 1, 2010
  • Rob

    Mark the files as embedded resources in Visual Studio

    Comment | October 28, 2010
  • Aleks

    Hi Rob,
    I am was thinking to build new MVC 3.0 web site using your approach looks like it is uncomputable with MVC 3.0 :( .

    Is there any chance to get from you some kind of guide how to make it work with MVC 3.0.

    Comment | November 15, 2010
  • Aleks

    Oops, looks like i am get it build with MVC 3.0, there was two small changes(but i dont know will it work or not yet).

    Another question, if i have MVC web and if i create plugin then on build action i copy the Plugin to the plugin directory of my web site and then some how i should say to my web site look in to this directory for controllers if request for plugin action comes…?

    Comment | November 15, 2010
  • tuck

    I am working on the MVC 3 update but I will be using the Razor view engine so somethings will be a little different but overall it will be the same. I’ll keep you all posted when I finish the update and how to make the changes.

    Comment | August 12, 2011
  • [...] just fine until I get to the building of the menu. The Menu is a plugin of its own. I have used http://www.thegecko.org/index.php/2010/06/pluggable-mvc-2-0-using-mef-and-strongly-typed-views/comme…. So far all is well like I mentioned but when I go to render the menu in the layout it pops an [...]

    Pingback | September 22, 2011
  • [...] a little bit of Googling I stumbled upon this article http://www.thegecko.org/index.php/2010/06/pluggable-mvc-2-0-using-mef-and-strongly-typed-views/ which was pretty much what we were looking for however it just needed to be ported from MVC2 to [...]

    Pingback | February 18, 2012
  • Le

    awesome post, I’ve taken the liberty to port this to MVC3 using latest version of Ninject from NuGet. You can easily swap out your choice of IoC e.g. Mef if preferred.

    http://blog.longle.net/2012/02/18/building-a-composite-mvc3-application-with-ninject/

    Comment | February 21, 2012
  • Asaf

    Hi
    im using razor and i came across some problems
    converting the application.
    the menu template seems to throw an exception

    he type or namespace name ‘Plugins’ does not exist in the namespace ‘Web’

    i would appriciate your help
    Thanks

    Comment | February 28, 2012
  • So, how do you add an “Assembly” directive to you view, when the view engine is Razor, not Web Forms?

    Comment | May 28, 2012
  • GokmenK

    Hi,

    Thank for your article and sample project. I want to write a programme based on your sample. But I want to seperate plugins as a view project(Web), model project(class library), controller project(class library) also. I seperated them like a hierarchy shown below then I have an error(CompositionContractMismatchException).

    Error:
    System.ComponentModel.Composition.CompositionContractMismatchException was unhandled by user code
    Message=Cannot cast the underlying exported value of type ‘Web.Plugins.Controllers.Presentation.HomeController (ContractName=”System.Web.Mvc.IController”)’ to type ‘System.Web.Mvc.IController’.

    Project Folder Hierarchy:
    /Core
    /—->Utilities
    /Plugins
    /—->Controllers
    /———->Web.Plugins.Controllers.Authentication
    /———->Web.Plugins.Controllers.Controls
    /———->Web.Plugins.Controllers.Presentation
    /———->Web.Plugins.Controllers.Reports
    /—->Models
    /———->Web.Plugins.Models.Authentication
    /———->Web.Plugins.Models.Controls
    /Web.Plugins.Authentication
    /Web.Plugins.Controls
    /Web.Plugins.Presentation
    /Web.Plugins.Reports
    /Web.Site

    When I move the models and controllers project to the root location, it works correctly :) Where must I change in the code? Or why I have getting this error? Help pls.

    Comment | April 1, 2014

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress | Design: TheBuckmaker.com WordPress Themes | Modifications: theGecko