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.