MEF Part 3 – Life cycle management and monitoring

Part 1 took a detailed look at binding of composable parts. In an application, however, we sometimes need to selectively break such bindings without deleting the entire container. We will look at interfaces which tell parts whether binding has taken place or whether a part has been deleted completely.

The IPartImportsSatisfiedNotification interface

For parts, it can be helpful to know when binding has taken place. To achieve this, we implement an interface called IPartImportsSatisfiedNotification. This interface can be implemented in both imports and exports.

[Export(typeof(ICarContract))]
public class BMW : ICarContract, IPartImportsSatisfiedNotification
{
    // ...
    public void OnImportsSatisfied()
    {
        Console.WriteLine("BMW import is satisfied.");
    }
}
class Program : IPartImportsSatisfiedNotification
{
    [ImportMany(typeof(ICarContract))]
    private IEnumerable<Lazy<ICarContract>> CarParts { get; set; }
 
    static void Main(string[] args)
    {
        new Program().Run();
    }
    void Run()
    {
        var catalog = new DirectoryCatalog(".");
        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
        foreach (Lazy<ICarContract> car in CarParts)
            Console.WriteLine(car.Value.StartEngine("Sebastian"));
        container.Dispose();
    }
    public void OnImportsSatisfied()
    {
        Console.WriteLine("CarHost imports are satisfied.");
    }
}

Sample 1 (Visual Studio 2010) on GitHub

When the above program is run, after executing container.ComposeParts() (line 14) the method OnImportsSatisfied() of the host will be executed. If this is the first time an export has been accessed, the export will first run the constructor, then its OnImportsSatisfied() method, and finally its StartEngine() method.

If we don’t use the Lazy<T> class, the sequence in which the methods are called is somewhat different. In this case, after executing the container.ComposeParts() method, the constructor, and then the OnImportsSatisfied() method will first be executed for all exports. Only then the OnImportsSatisfied() method of the host will be called, and finally the StartEngine() method for all exports.

Using IDisposable

As usual in .NET, the IDisposable interface should also be implemented by exports. Because the Managed Extensibility Framework manages the parts, only the container containing the parts should call Dispose(). If the container calls Dispose(), it also calls the Dispose() method of all of the parts. It is therefore important to call the container’s Dispose() method once the container is no longer required.

Releasing exports

If the creation policy is defined as NonShared, multiple instances of the same export will be created. These instances will then only be released when the entire container is destroyed by using the Dispose() method. With long-lived applications in particular, this can lead to problems. Consequently, the CompositionContainer class possesses the methods ReleaseExports() and ReleaseExport(). ReleaseExports() destroys all parts, whilst ReleaseExport() releases parts individually. If an export has implemented the IDisposable interface, its Dispose() method is called when you release the export. This allows selected exports to be removed from the container, without having to destroy the entire container. The ReleaseExports() and ReleaseExport() methods can only be used on exports for which the creation policy is set to NonShared.

In the following example, the IDisposable interface has been implemented in each export.

using System;
using System.ComponentModel.Composition;
using CarContract;
namespace CarBMW
{
    [Export(typeof(ICarContract))]
    public class BMW : ICarContract, IDisposable
    {
        private BMW()
        {
            Console.WriteLine("BMW constructor.");
        }
        public string StartEngine(string name)
        {
            return String.Format("{0} starts the BMW.", name);
        }
        public void Dispose()
        {
            Console.WriteLine("Disposing BMW.");
        }
    }
}

The host first binds all exports to the import. After calling the StartEngine() method, we use the ReleaseExports() method to release all of the exports. After re-binding the exports to the import, this time we remove the exports one by one. Finally, we use the Dispose() method to destroy the container.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using CarContract;
namespace CarHost
{
    class Program
    {
        [ImportMany(typeof(ICarContract), RequiredCreationPolicy = CreationPolicy.NonShared)]
        private IEnumerable<Lazy<ICarContract>> CarParts { get; set; }
 
        static void Main(string[] args)
        {
            new Program().Run();
        }
        void Run()
        {
            var catalog = new DirectoryCatalog(".");
            var container = new CompositionContainer(catalog);
 
            container.ComposeParts(this);
            foreach (Lazy<ICarContract> car in CarParts)
                Console.WriteLine(car.Value.StartEngine("Sebastian"));
 
            Console.WriteLine("");
            Console.WriteLine("ReleaseExports.");
            container.ReleaseExports<ICarContract>(CarParts);
            Console.WriteLine("");
 
            container.ComposeParts(this);
            foreach (Lazy<ICarContract> car in CarParts)
                Console.WriteLine(car.Value.StartEngine("Sebastian"));
 
            Console.WriteLine("");
            Console.WriteLine("ReleaseExports.");
            foreach (Lazy<ICarContract> car in CarParts)
                container.ReleaseExport<ICarContract>(car);
 
            Console.WriteLine("");
            Console.WriteLine("Dispose Container.");
            container.Dispose();
        }
    }
}

The program output therefore looks like this:

CommandWindowSample02

Sample 2 (Visual Studio 2010) on GitHub

Author: Stefan Henneken

I’m Stefan Henneken, a software developer based in Germany. This blog is just a collection of various articles I want to share, mostly related to Software Development.

Leave a comment