Wednesday, December 30, 2009

PRISM, MEF, and MVVM Part 2 of 3: Making PRISM MEF Friendly

In the first part of this series, we explored using the Unity container to bind the view model to the view. The next logical step is to explore how to use MEF. PRISM provides several useful mechanisms that relate directly to views and modules, such as the region manager and the module manager. This allows us to dynamically assign views to regions on the display and dynamically load modules.

Logically, it seems we could create a MEF module, dynamically load it through PRISM, and glue its parts together as well. We'd probably start with a view model that looks like this:


[Export]
public class MEFViewModel 
{
    [Import]
    public IService { get; set; }

    public DelegateCommand<object> Command { get; set; }
}

And then easily pull it into a view like this:


public partial class MEFView : UserControl
{
   [Import]
   public MEFViewModel ViewModel { get; set; }

   public MEFView() 
   {
      InitializeComponent();
      PartInitializer.SatisfyImports(this);
      LayoutRoot.DataContext = ViewModel; 
   }
}

That is the essence of how it works, right?

Unfortunately, if you try this (like I did) you'll quickly find that your parts won't compose. You'll receive errors, and the view won't render. The view model won't get instantiated. Why is this? Where did we go wrong?

Stable Composition

The first thing to understand before we can start fixing things is a concept known as "Stable Composition." This feature, which is described in detail in Glenn Block's blog entry Stable Composition in MEF, is a fail-safe used to prevent unstable parts from being composed into your application. The pro is that if a failure exists somewhere down the chain of imports and exports, the part is simply discarded and not considered for import. The con is that it takes a little bit more debugging and troubleshooting to discover exactly why the composition failed. In this case, we were unable to find an import for IService, so the view model could not be imported into the view, which caused the PartInitializer to complain because, well, it couldn't "get no satisfaction."

If you recall in part 1, we flagged our service implementation with the Export property. What went wrong?

MEF and Silverlight XAP files

One frustration I've had with a lot of online examples of using MEF with Silverlight is that everything is contained within a single XAP file. For larger, scalable, and pluggable applications, this simply won't be the case. This makes straightforward examples suddenly break when you start to distribute your classes between various projects or PRISM modules.

The PartInitializer class is a helper class in MEF that helps compose parts where they might not normally be seen. By this I mean in pages like user controls that are constructed by the XAML parser. The XAML parser doesn't understand MEF, so it is unaware of how to reach out and satisfy imports. By including the PartInitializer in the constructor and calling the SatisfyImports method with the view, this allows MEF to inspect the view and XAML-generated parts and begin composition.

By default, however, the PartInitializer only "knows" about the current XAP file. It has no way of knowing what other assemblies and XAP files exist in the project. In our example, because the interface for the service and implementation both exist in separate projects (and XAP files), MEF choked on the import because it did not know where to find the export.

The Silverlight 4 toolkit includes the PackageCatalog. This facilitates dynamic loading of XAP files as well as composition using different XAP files. In Silverlight 3, without the benefit of this type of catalog (which may become available in future previews), we must build our own infrastructure to facilitate the composition across XAP boundaries. (Tip: if you want to use the package catalog in Silverlight 3, the source is available for the toolkit).

Introducing the Catalog Service

To begin, we need a service for the modules to communicate with and help aggregate catalogs between XAP files. This is the piece that Glenn Block helped build and explained to me, so many thanks to him. Be sure to subscribe to his blog and Twitter feed, lots of useful information there!

We start with a simple definition:


public interface ICatalogService 
{
   void Add(ComposablePartCatalog catalog); 
}

The ComposablePartCatalog is the base catalog class that allows us to add catalogs to our service.

In the implementation of the service, we use an AggregateCatalog to hold the other catalogs in one place:


public class CatalogService : ICatalogService
{
    private AggregateCatalog _catalog;

    public CatalogService(AggregateCatalog catalog)
    {
        _catalog = catalog;
    }

    public void Add(ComposablePartCatalog catalog)
    {
        _catalog.Catalogs.Add(catalog);
    }
}

Notice that the constructor expects us to pass a catalog. Where does this catalog come from?

This is where things will start to get a little interesting, so bear with me.

First, in our bootstrapper, we'll want to aggregate all of the existing parts into one catalog. Let's take a look at a new method GetHostCatalog that creates a new AggregateCatalog and returns it:


private AggregateCatalog GetHostCatalog()
{
    var catalog = new AggregateCatalog();

    foreach (AssemblyPart ap in Deployment.Current.Parts)
    {
        StreamResourceInfo sri = Application.GetResourceStream(new Uri(ap.Source, UriKind.Relative));
        
        if (sri != null)
        {
            Assembly assembly = ap.Load(sri.Stream);        
            catalog.Catalogs.Add(new AssemblyCatalog(assembly));
        }
    }

    return catalog;
}

So the "deployment" is the active Silverlight application. We iterate each part, then stream the actual assembly. The assembly is then added to the catalog. This will take care of all of the assemblies available when the bootstrapper runs. Let's wire that in and then see how we can grab the dynamic modules as well.


private void InitializeCompositionHost()
{
    var catalog = GetHostCatalog();
    var container = new CompositionContainer(catalog);
    container.ComposeExportedValue<IService>(Container.Resolve<IService>()); 
    container.ComposeExportedValue<IRegionManager>(Container.Resolve<IRegionManager>()); 
    Container.RegisterInstance<ICatalogService>(new CatalogService(catalog));
    CompositionHost.InitializeContainer(container);
}

Now we are starting to see pure play between Unity and MEF. First, we start the catalog. Next, we create a composition container using the catalog. Now we can interface between Unity and MEF using the ComposeExportedValue method. Here, I am providing MEF with an explicit export for a given import, and using the Unity container ("big C") to resolve the dependencies. This will give us the exports for IService and IRegionManager. The Unity container is also given a single instance of ICatalogService.

Finally, a very key step is to tell MEF to use our special container. One of the features of MEF is that it effectively hides the container from your view. You can literally build applications using nothing other than the Import and Export attributes. However, sometimes we need to explicitly manage or configure the container. This is one of those cases: we've built our own container with the aggregate catalogs and even provided explicit hints to the container for marrying imports and exports. To allow the PartInitializer to use our container, we register it through a static method on the composition host. This is the InitializeContainer call you see, passing it the MEF container ("little c").

All of this happens in the bootstrapper. We'll call the InitializeCompositionHost from an override used to configure the Unity container: ConfigureContainer.

Now we've set up the initial application and configured our container. We still need a way to register dynamic modules as they are loaded. For this, we'll go to our common project and make a new base class for modules that will play in the MEF space. Instead of basing these modules on IModule, we'll base them on the new base class that implements IModule, called PartModule. This is what the part module base class looks like:


public abstract class PartModule : IModule
{
    [Dependency]
    public ICatalogService CatalogService { get; set; }

    public virtual void Initialize()
    {
        var assembly = this.GetType().Assembly;

        var types = assembly.GetTypes().Where(t => !typeof(IModule).IsAssignableFrom(t));

        CatalogService.Add(new TypeCatalog(types));
    }
}

First, we go ahead and implement IModule as is required by PRISM for a module. Next, notice that we let Unity provide us our singleton ICatalogService instance by providing a property and decorating it with the Dependency attribute.

Next, we implement Initialize and grab the assembly. We grab all of the types available in the assembly and add them to our aggregate catalog using a type catalog. This literally makes all types in the current assembly available for export. Notice, however, we ignore IModule ... we don't want to create a circular reference or dependency and accidentally import the module itself!

Now we have all of the pieces in place to make PRISM friendly with MEF. We can now export and import across XAP boundaries and even handle dynamically loaded modules (in this case, we're letting PRISM handle loading and managing the modules).

There is no source for this entry because there is nothing to show yet. In the next and final installment, I will create a dynamic module based on MEF and complete the loop by demonstrating yet another dynamically loaded module along with a dynamically inserted view, all using MEF to resolve dependencies for us.

Jeremy Likness

4 comments:

  1. Hello

    Thanks for this. But I've a question : What's the difference between your method, and using the MefContrib project to integrate MEF into Unity ?

    Piotr Włodek made a special version of mefcontrib to use it with silverlight :
    http://pwlodek.blogspot.com/2009/11/mef-contrib-for-silverlight-3.html

    This question could be stupid but I'm just started with MEF + PRISM + UNITY.

    ReplyDelete
  2. The only "stupid questions" are the ones never asked. It's a good question. It's like math in school ... you learn math, but then you can use a calculator ... the tools/contribs are calculators, this is math. The intent of the series is to illustrate what can be done and how to do it; to develop your own solutions should the existing ones not fit exactly. Thanks!

    ReplyDelete
  3. Good post Jeremy, I'm a little confused on this part:

    public virtual void Initialize()
    07.
    {
    08.
    var assembly = this.GetType().Assembly;
    09.

    10.
    var types = assembly.GetTypes().Where(t => !typeof(IModule).IsAssignableFrom(t));
    11.

    12.
    CatalogService.Add(new TypeCatalog(types));
    13.
    }



    You said that you grab all the types available for export....but it looks like it's just taking _all_ types period aside from the IModule implementers. Is that because the assembly that you have in hand only contains exportable types by this time? Is there some "magic" in there?\

    Thanks!
    Josh

    ReplyDelete
  4. The "magic" is in the TypeCatalog. We want the application to grow as needed, so we parse all of the valid types and then let MEF handle which are actually exports. This is part of MEF and is done inside the TypeCatalog (line 12). It will parse all of the types looking for exports, and only bring the types into the catalog that are valid exports.

    ReplyDelete