The first part of the article was about getting familiar with MEF (Managed Extensibility Framework) – to get to know the basics, define its core principles and apply those to an existing Gallery application to make it configurable through themes. This part will dig deeper into the framework.
MEF as an IoC Container?
MEF is not a replacement for an IoC container - they both address different scenarios and can be used in the same application. However, MEF sure can be used for dependency injection without having to resort to a proper IoC container. Take a look at Gallery’s MainPageViewModel, for example:
public class MainPageViewModel : ViewModelBase
{
private FlickrPhotoProvider photoProvider;
public MainPageViewModel()
{
photoProvider = new FlickrPhotoProvider();
// Code
}
// Code
}
FlickrPhotoProvider class in the above code snippet is responsible for calling the Flickr API. It’s got a few properties and a method for getting the photos from the service. What we want is to take this class out of MainPageViewModel and inject it to ViewModel’s through its constructor – a job, usually handled by an IoC container. With MEF, however, the injection is done by importing the class into ViewModel’s constructor. The FlickrPhotoProvider class is marked for export:
[Export(typeof(IPhotoProvider))]
public class FlickrPhotoProvider : IPhotoProvider
{
// Code
}
… while ViewModel’s constructor, taking the class as a parameter, is marked with the ImportingConstructor attribute:
[ImportingConstructor]
public MainPageViewModel(IPhotoProvider photoProvider)
{
this.photoProvider = photoProvider;
// Code
}
Note that in the above example I’m no longer using the concrete FlickrPhotoProvider type. Because I may want to inject other photo providers at different times, I extracted an IPhotoProvider interface from it and used that. Regardless, the ImportingConstructor attribute will take care of injecting the discovered provider class into the ViewModel.
Condition-based injection
In fact, there may be times when I’d like to show the Gallery to people but have no access to the internet. Wouldn’t it be great if the application could pick that up and automatically display some sample images instead of photos from Flickr? No problem! When no network is present, we’re going to make the application use a different photo provider that reads images from a hosting web site.
MEF Metadata was already discussed in the first part of the article – here I’m going to use the same principle, but without declaring a new metadata attribute (just as an example – I’m using “magic strings” here, which are prone to typing errors and therefore considered evil). But anyway, this is how each of the photo provider classes could be decorated to indicate the situation they were supposed to be used in:
[Export(typeof(IPhotoProvider))]
[ExportMetadata("IsOnline", true)]
public class FlickrPhotoProvider : IPhotoProvider
{
// Code
}
[Export(typeof(IPhotoProvider))]
[ExportMetadata("IsOnline", false)]
public class LocalPhotoProvider : IPhotoProvider
{
// Code
}
Because we’re now dealing with multiple imports, we need to collect all of the IPhotoProvider exports and choose the one with the IsOnline metadata value corresponding to the the value that is returned by the NetworkInterface.GetIsNetworkAvailable() method. For that, we’re going to use a PhotoProviderService:
[Export]
public class PhotoProviderService
{
[ImportMany]
public IEnumerable<Lazy<IPhotoProvider, IOnlineMetadata>> PhotoProviders { get; set; }
public IPhotoProvider PhotoProvider
{
get
{
return (from p in PhotoProviders
where p.Metadata.IsOnline == NetworkInterface.GetIsNetworkAvailable()
select p.Value).FirstOrDefault();
}
}
}
This service exposes the PhotoProvider property returning the appropriate photo provider, based on its IsOnline metadata value, provided by the IOnlineMetadata:
public interface IOnlineMetadata
{
bool IsOnline { get; }
}
To inject the service into the ViewModel, the constructor needs to take it as a parameter:
[ImportingConstructor]
public MainPageViewModel(PhotoProviderService photoProviderService)
{
photoProvider = photoProviderService.PhotoProvider;
// Code
}
Here… PhotoProviderService class now acts as a configuration module (or a locator) for discovering photo providers and returning the one we want at a given time.
Discovery beyond the default XAP package
It’s not that uncommon that you have to provide some kind of optional component to your application – be that an administration/configuration module, a UI/form designer or similar; you would ship those modules to selected customers/installs only, and the application should be ready to act on its (non)presence. I remember solving this problem many times with different developing tools/platforms, but never was it so easy as it’s now by using MEF. With Silverlight application, we would probably want to pack this optional component as a separate XAP package, that would be downloaded on demand (that is – if it existed, and if the user had rights to download it). Projecting this onto the Gallery application, we’re going to provide a configuration panel where user could change some of theme’s properties. And that panel is going to be packaged into a new XAP.
The scope for part discovery in Silverlight’s MEF implementation is – by default – the application’s XAP package (or more correctly – the assemblies contained in that XAP). I said ‘by default’ because the scope can easily be changed. MEF finds the imports through catalogs. The default catalog in Silverlight is aggregated from one or more assembly catalogs, each catalog corresponding to an assembly in the XAP. An assembly catalog is responsible for keeping references of all the exports found in a single assembly.
Taking a look over to the latest Silverlight Toolkit discovers a new kind of a catalog – the PackageCatalog. Together with the accompanying Package class, it can be used to asynchronously download a XAP package from the server, add that package to the existing catalog, to get the application to recompose itself – that is – all the imports would be reevaluated for missing/added exports. Sounds perfect for the job!
But first, we have to design a ConfigurationWindow.
ConfigurationWindow could easily be made exportable by decorating the class with the Export attribute. The problem is that it derives from the ChildWindow class, and that would make all ChildWindows exportable -something we don’t want. Instead, we need to provide additional information about the class to make it stand out from the crowd – we need to name the export so it could be invoked for importing by that same name:
[Export("Configuration")]
public partial class ConfigurationWindow : ChildWindow
{
// Code
}
This provides a lot of flexibility, but we’re using “magic strings” again. If using custom attribute was a solution for strong-typing the metadata, contract name here should at least be replaced with constants, defined in an assembly, shared between the project (Gallery.Extensibility in this case).
Another thing with delayed packages download is that we need to tell MEF about it – we have to plan for recomposition. There are really two things to do:
- allow imports to not be satisfied on initial composition (allow default value on composition)
- allow for recomposition
Conveniently enough, both can be done in a single Import declaration:
[Import(PartContracts.Configuration, AllowDefault = true, AllowRecomposition=true)]
public Lazy<ChildWindow> ConfigurationWindow
{
get { return configurationWindow; }
set
{
if (configurationWindow == value)
{
return;
}
configurationWindow = value;
OnPropertyChanged("ConfigurationWindow");
}
}
This import is happening on yet another service – DialogWindowService is used for invoking dialog windows and will be injected into the ViewModel as well.
When ready, ConfigurationWindow is packaged into a separate XAP and placed on the server, waiting to be downloaded. To actually use the PackageCatalog, the default MEF behavior has to be changed by overriding the composition container. After container is initialized, the XAP download can be initiated by calling a single method:
Package.DownloadPackageAsync(new Uri("PhotoGallery.Configuration.xap", UriKind.Relative),
(e, p) =>
{
if (p != null)
{
catalog.AddPackage(p);
}
});
When package is downloaded, recomposition happens, triggering a series of events involving the ViewModel and its ShowConfigurationCommand. The Options button, located on the main page and bound to the ShowConfigurationCommand, finally lights up, enabled.
Final touches
The last thing I’m going to add to the Gallery is some special effects. Like snowing for Christmas theme, or some lightning for the Halloween. It’s a simple trick anyway. In the main page, the topmost control position is reserved for a Canvas control. This Canvas is used for hosting a behavior that comes with the extended theme:
public interface ITheme
{
string SearchString { get; }
Color Foreground { get; }
Color Background { get; }
BitmapImage Pushpin { get; }
Behavior<Canvas> BehaviorEffect { get; }
}
The BehaviorEffect behavior is attached to the canvas on composition (where canvas is named front and theme is the imported theme):
Interaction.GetBehaviors(front).Add(theme.BehaviorEffect);
As we see, all kinds of objects can be used with MEF composition. And if you’re doing ViewModels with MVVM pattern – yes, those are exportable too.
[Export]
public class MainPageViewModel : ViewModelBase
{
// Code
}
public partial class MainPage : UserControl
{
[Import]
public MainPageViewModel ViewModel
{
get { return DataContext as MainPageViewModel; }
set { DataContext = value; }
}
// Code
}
The end
In this two part series I tried to introduce the basic concepts of MEF on an actual application by walking you through the process of converting some of its parts to benefit from MEF in different ways. I hope you found it interesting enough to try MEF for yourself (if not already done so) – it’s definitely going to change the way we look at modular applications today.
The application is available to run here and the source code will be posted on Codeplex shortly.
Comments are welcome.