Sunday, March 28, 2010

MEF and IoC/DI (4)

Let's look my first API class - DIResolverBase.

DIResolverBase Class


First, I need to create a class or a library project to define DI mapping relationships which will be used in my application. For example, DIResolver class is a class where I define MEF Import and Export properties. When the class is loaded as an assembly catalog or directory catalog to MEF, the parts or Import and Export mapping relationships are then loaded to MEF framework.

DIResolverBase class is a base class for DIResolver. The class is very simple. It implements IDisposable interface and contains a protected Add() method for holding disposable items.

When I started to use MEF, I was confused by the concept of disposing instances I retrieved from MEF or injected by MEF. At the start, I thought that an instance being requested from MEF was created by MEF and hence MEF should dispose them on application termination. However, it did not happen. In some cases, I had to explicitly dispose the instances retrieved from or injected by MEF in my application; otherwise, some resources such as files or connections would be left unclosed when my application is terminated.

Finally, I figured it out. It is wrong that I thought MEF created requested/injected instances, even MEF does create some instances in my DI library. It'll be much easier to explain it in an example: a console application and a DI library with a DIResolver class. The application only knows IProcessControler interface. The mapped instance is retrieved from the DI library. Here is a picture how the application and MEF work:


and here are some DI codes:

internal class DIResolver : DIResolverBase
{
  [Export]
  private IProcessController _exportController {
  get {
    var instance = new MyController( _log, _reader );
    // Add disposable obj to base created by this class
    // so it will be cleaned up when app terminates
    base.Add(instance);
    return instance;
  }

  [Import]
  private IDataReader _reader { get; set; }

  [Import]
  private ILog _log { get; set; }

}

At the start, the DI library is loaded to MEF as an assembly catalog. Then an instance of IProcessController is requested by the application (1). On the request, MEF resolves the mapping instance by creating an instance of DIResolver class because it found the Export for this type there (2). Hence the Import properties are initialized (3) with the injected instances (Export properties are defined in other DI libraries). Finally, a new the instance is created in the Export property getter (4). In this sense, MEF does create the instance of DIResolver class but not the instance of IProcessController. It is the DIResolver class that creates or news the instance. Therefore the clean up job should be done in this class.

Fortunately, MEF does the nice and cleaning up job when the application is terminated or the MEF container is disposed. MEF will call the dispose() method on all the composable parts, including DIResolver which is based on DIResolverBase. The base class provides the change of disposable.

The protected Add() API in the base class provide a way for the driving class to add any created and to-be-disposed instance to the base. The base class will do the cleaning up job on termination. In above example, you can see the customized DIResolver class is very simple and clean. In case of some instances which do not implement IDisposable interface but do have some resources to be released by other interfaces such as Close() or Disconnect(), the DIResolver class should override Dispose() method, do the customized cleaning job there, and call the base Dispose() method if any disposable instances were added.

One interesting feature of DIResolver class is that it can be an internal type class and the Import and Export properties can be private. This class is mainly used for MEF and rarely used as a public API. So I define it as a none-accessible class from the DI library. It is amazing that MEF can figure it out, i.e., to create DIResolver instances and to access to the private properties.

0 comments: