Metro version of RAGE to leverage Managed Extensibility Framework

In the Alpha/Silverlight 4 version of RAGE, developers can create their own ScriptAction classes to extend the RageEngine environment. Unfortunately, the current setup requires that each custom class be registered manually. In the WinRT/Metro version that's all going to change. In fact, many of the Environment's current limitations and assumptions will be eliminated with the adoption of the Managed Extensibility Framework (MEF).

If you're unfamiliar with MEF and what it is/does, it provides a very straightforward method of achieving "plug and play" extensibility in your application by using Import and Export attributes. A host application/assembly specifies the types it can import, and any classes that export those same types can be catalogued and instantiated by the host. But that's not the best part — the exported classes can be in one or many external assemblies that aren't even hard-referenced by the host.

MEF diagram

The above diagram illustrates how a host imports internal classes (within the same assembly) as well as classes in external assemblies. They all export IModule, thus the host will assemble an "aggregate catalog" containing all the eligible types. Note: though the host is now aware of the types, concrete instances won't be created until the need arises (lazy instantiation).

An extremely basic example
Download: WPF_MEF.zip (90kb)
This is a a simple WPF application that demonstrates MEF in action. The first thing to note is the added reference to System.ComponentModel.Composition, the namespace where MEF functionality lives in .NET Framework 4. Let's have a quick look at the first few lines of MainWindow's code-behind.

public partial class MainWindow : Window
{
    [ImportMany(typeof(IModule))]
    private IEnumerable<IModule> _modules;

The "_modules" member variable will serve as the container for all types imported during the composition process. In MainWindow's constructor we call a private method called ImportModules:

private void ImportModules()
{
    // an aggregate catalog that combines multiple catalogs
    var catalog = new AggregateCatalog();

    // check the current assembly's execution directory for other assemblies 
    with matching Export types
    catalog.Catalogs.Add(new DirectoryCatalog(
         System.AppDomain.CurrentDomain.BaseDirectory)); 

    // create the CompositionContainer with the parts in the catalog
    CompositionContainer container = new CompositionContainer(catalog);

    // fill the imports of this container object
    container.ComposeParts(this);
}

The above snippet is about as difficult as MEF gets, which is to say not difficult at all! We established an aggregate catalog and told MEF where to look for eligible assemblies. Remember that we're not limited to the current assembly — we can specify other directories just as easily. For simplicity's sake I've kept everything scoped to the host assembly's "bin" folder. If you decide to experiment with the sample project and change the Modules assemblies, you'll need to manually copy them to the host's bin in order for the changes to take effect.

Note that the Interfaces assembly is separate from both the host and Modules assemblies. This eliminates the need for any hard references between the host and the Modules assembly.

And finally, a look at the first few lines of ModuleA.

[Export(typeof(IModule))]
public class ModuleA : IModule
{
... [ ctor and interface implementation ]
}

... and that's all it takes for ModuleA to be eligible for discovery. Not a whole lot of code but a huge bang for the buck. But as you can imagine, this only scratches the surface of MEF. Export attributes can also pass detailed metadata, object creation policies can be specified, and much more. Those wanting to deep-dive may want to start here: http://msdn.microsoft.com/en-us/VS2010TrainingCourse_IntroToMEF.

The reason I bothered to blog it: RAGE is about to reap some major benefits from this. Developers will no longer need to manually register ScriptActions (er, ScriptModules, I better get in that habit now). As long as they decorate their classes with [Export(typeof(IScriptModule))], RAGE will take it from there.

But really, why stop at ScriptModules? This same approach can be applied to scene and character controls as well. RAGE currently requires you to specify the folders where scenes and characters reside. With MEF implemented it won't matter where they sit — using the proper Export attributes is all that matters.

More details will surely come when RAGE (Metro) goes Alpha and the documentation follows. For now it excites me to know that RAGE will soon be much more developer-friendly.

Posted: 11/11/2011 9:18:46 PM

Comments
There are no comments for this article.
Add Comment
Name (optional):
Comment:
Verify:
Articles By Subject
ASP.NET
Buddy Knavery Episode I
Buddy Knavery Episode II
Checksum Labs
LOST Redux
Miscellaneous
Retro Adventure Game Environment
Silverlight
Steel Saga
WinRT/Metro