Silverlight 4 Beta & MEF – Experimenting with an alternative Programming Model

One of the things that seems hard-baked into MEF if you watch the Silverlight 4 and MEF screencasts that I’ve made is that MEF relies on attributes. It’s common to see an example such as this skeleton class;

  public class PluginManager
  {
    public PluginManager()
    {
      PartInitializer.SatisfyImports(this);
    }
    [ImportMany]
    public IEnumerable<IPlugin> Plugins { get; set; }
  }

which is using an ImportMany attribute so if it happens to be composed with these 2 types below;

 [Export(typeof(IPlugin))]
  public class PluginA : IPlugin
  {
    public string Name
    {
      get { return ("PluginA"); }
    }

    public void DoSomething()
    {
      throw new NotImplementedException();
    }
  }
  [Export(typeof(IPlugin))]
  public class PluginB : IPlugin
  {
    public string Name
    {
      get { return ("PluginB"); }
    }

    public void DoSomething()
    {
      throw new NotImplementedException();
    }
  }

then creating a new instance of PluginManager would result in MEF composing an instance of PluginA and PluginB onto/into that property called Plugins on PluginManager.

However, the attributes aren’t essential to MEF, it can manage perfectly well without them – they form part of what MEF calls a “Programming Model” and the attributed programming model is the one that ships in the box and is pretty succinct in use but you can replace it with your own or someone else’s and even put different programming models side-by-side.

I wanted to experiment with this and so I wrote a few classes as a way of furthering that exploration.

What I’ve done is (mostly, not entirely) untested and incomplete. I’ve defined my composable parts and their imports/exports in a separate XAML definition that could either then be loaded from a file ( i.e. synchronously from a resource or asynchronously over the network ) or just embedded as a resource in the application.

I started by writing a catalog class derived from ComposablePartCatalog and I decided to call it StaticCatalog which was probably a bad name as the word Static then got stuck all over my code. However, it let me reproduce a similar scenario to the one given in the previous code snippets with this lump of XAML which I’ve dropped into my application resources;

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             x:Class="SilverlightApplication44.App"
             xmlns:sc="clr-namespace:StaticProgrammingModel;assembly=XamlProgrammingModel">
    <Application.Resources>
        <sc:StaticCatalog x:Key="catalog">
            <sc:StaticPartDefinition
                PartType="SilverlightApplication44.PluginManager,SilverlightApplication44,Version=1.0.0.0">
                <sc:StaticPartDefinition.Exports>
                    <sc:StaticExportTypeDefinition />
                </sc:StaticPartDefinition.Exports>
                <sc:StaticPartDefinition.Imports>
                    <sc:StaticImportPropertyDefinition
                        PropertyName="Plugins"
                        Cardinality="ZeroOrMore" />
                </sc:StaticPartDefinition.Imports>
            </sc:StaticPartDefinition>
            <sc:StaticPartDefinition
                PartType="SilverlightApplication44.PluginA,SilverlightApplication44,Version=1.0.0.0">
                <sc:StaticPartDefinition.Exports>
                    <sc:StaticExportTypeDefinition
                        ExportType="SilverlightApplication44.IPlugin,SilverlightApplication44,Version=1.0.0.0" />
                </sc:StaticPartDefinition.Exports>
            </sc:StaticPartDefinition>
            <sc:StaticPartDefinition
                PartType="SilverlightApplication44.PluginB,SilverlightApplication44,Version=1.0.0.0">
                <sc:StaticPartDefinition.Exports>
                    <sc:StaticExportTypeDefinition
                        ExportType="SilverlightApplication44.IPlugin,SilverlightApplication44,Version=1.0.0.0" />
                </sc:StaticPartDefinition.Exports>
            </sc:StaticPartDefinition>
        </sc:StaticCatalog>        
    </Application.Resources>
</Application>

Ok – so you’ll notice some horrible long type names. But what this lump of XML is basically saying is;

  • ComposablePart: PluginManager
    • Exports itself
    • Imports to a property called Plugins and expects ZeroOrMore imports
  • ComposablePart: PluginA
    • Exports itself as an IPlugin
  • ComposablePart: PluginB
    • Exports itself as an IPlugin

now I can strip the attributes off my code as in;

  public class PluginManager
  {
    public IEnumerable<IPlugin> Plugins { get; set; }
  }
  public class PluginA : IPlugin
  {
    public string Name
    {
      get { return ("PluginA"); }
    }

    public void DoSomething()
    {
      throw new NotImplementedException();
    }
  }
  public class PluginB : IPlugin
  {
    public string Name
    {
      get { return ("PluginB"); }
    }

    public void DoSomething()
    {
      throw new NotImplementedException();
    }
  }

and things still work pretty much like they did before but the eagle-eyed observer will notice that I’ve also removed the call to PartInitializer.SatisfyImports which was present in the constructor of my class PluginManager.

Why? Because PartInitializer.SatisfyImports is linked to the attributed programming model. When you pass it an instance, it uses types in System.ComponentModel.Composition.AttributedModel to figure out the attributes on the type and, in my case, I don’t have any attributes to be figured out so that won’t work.

However, I can still use a CompositionContainer and so if I write a little code somewhere ( this code from my App class and runs on start up );

 StaticCatalog catalog = (StaticCatalog)this.Resources["catalog"];

      CompositionContainer container = new CompositionContainer(catalog);

      PluginManager pluginManager = container.GetExportedValue<PluginManager>();

then the instance of PluginManager that I get returned will indeed have 2 instances of IPlugin ( one instance of PluginA and one of PluginB ) composed onto that Plugins property that it has.

Neat – not an attribute in sight. I have removed .NET attributes from my MEF code and replaced them with XAML definitions that are about 10 times longer to type out 🙂

Now, I could have mixed the attributed and non-attributed models in that I could have left PluginManager looking like this;

  public class PluginManager
  {
    public PluginManager()
    {
      PartInitializer.SatisfyImports(this);
    }
    [ImportMany]
    public IEnumerable<IPlugin> Plugins { get; set; }
  }

and then defined my XAML-based catalog as;

        <sc:StaticCatalog x:Key="catalog">            
            <sc:StaticPartDefinition
                PartType="SilverlightApplication44.PluginA,SilverlightApplication44,Version=1.0.0.0">
                <sc:StaticPartDefinition.Exports>
                    <sc:StaticExportTypeDefinition
                        ExportType="SilverlightApplication44.IPlugin,SilverlightApplication44,Version=1.0.0.0" />
                </sc:StaticPartDefinition.Exports>
            </sc:StaticPartDefinition>
            <sc:StaticPartDefinition
                PartType="SilverlightApplication44.PluginB,SilverlightApplication44,Version=1.0.0.0">
                <sc:StaticPartDefinition.Exports>
                    <sc:StaticExportTypeDefinition
                        ExportType="SilverlightApplication44.IPlugin,SilverlightApplication44,Version=1.0.0.0" />
                </sc:StaticPartDefinition.Exports>
            </sc:StaticPartDefinition>
        </sc:StaticCatalog>        

and then I can write code such as;

      StaticCatalog catalog = (StaticCatalog)this.Resources["catalog"];

      CompositionHost.InitializeContainer(new CompositionContainer(catalog));

      PluginManager pluginManager = new PluginManager();

and that lets me set up my StaticCatalog ( exports PluginA, PluginB ) as one of the catalogs in the default container and so the call to the constructor of PluginManager (and its subsequent call to PartInitializer.SatisfyImports()) will work again as the PluginManager is attributed.

It’s pretty clear that writing component catalogs this way in XAML is a lot more long-winded than just using the regular attribute model but I took it a reasonable way along in order to try to support various MEF features such as;

  • Exporting fields, properties and (somewhat) methods
  • Exporting by interface or base class
  • Choosing the creation policy on an export
  • Importing fields, properties, delegates but I haven’t implemented importing constructors at this point
  • Cardinality of imports
  • Lazy imports
  • PartCreator imports ( i.e. where you import a function that knows how to create the actual import )
  • Recomposability
  • Choosing the creation policy on an import
  • Applying metadata around exports (although I only went so far as to deal with dictionaries of <string,string> rather than <string,object>. – i.e. I didn’t build any way to specify the type of a value in a KeyValuePair into my XAML definition.

there’s a bunch of help already in the framework when you’re working with types – specifically the types in System.ComponentModel.Composition.ReflectionModel and also the AttributedModelServices class so those mostly do the heavy lifting for me.

Note – as I said at the top of the post, this was me experimenting rather than me trying to tell anybody how to build a Programming Model – it’s my first attempt so not likely to be the best effort anyone’s ever made 🙂

Here’s some examples of a few “scenarios” using this model. To avoid pasting lots and lots of code into the blog post, I put the code into a zip file and used the viewer that I published in this previous post to bring them together below – I’ve tried to include snippets of code for a few scenarios with this programming model;

If you want the code for the programming model bits then that’s here for download but bear in mind that it was cooked up reasonably quickly as part of my own experimenting with programming models – there’s a tiny application in the solution as well which uses the StaticCatalog to import a set of plugins which provide UI content.