Sunday, March 14, 2010

MEF and IoC/DI (2)

Let's explore how to use MEF as a DI framework.

Define Object Mapping Relationships


The documentation at CodePlex MEF project provides excellent information about its MEF structure, how to use it and examples. The information over there may be overwhelming, or more than you need if terms of DI. Here I'll explain MEF briefly just in the area of how to use MEF as a DI framework.

In MEF, the basic units are called as Composable Parts. A part can be either Export or Import. In the case of DI, this can be used to describe mapping relationships.


An Export is used to describe a composable part as a mapping relationship, for example, a class to a class type or interface type. In other words, it tells MEF how to get or create an instance for a class or an interface type.

An Import part, one the other hand, provides a gate way for a client get an instance as a class type or an interface type from MEF. For an import, MEF will inject an instance for a property or field variable if MEF has the knowledge of the corresponding Export part.

In MEF framework, Export and Import are used as attributes. Export attribute can be used to mark a class, a property or a method, while Import attribute can be used to a property, a field value or a constructor parameter.

In my DI practice, I use Export to define mapping relationships, and use Import in codes to let MEF to inject instances based on Export mappings. I mostly use Export/Import for property or field values, rarely for classes, methods or CROR parameters. One reason is that in most cases, I use third part components or reuse components I created before I knew MEF, therefore I cannot add Export/Import attributes to those classes or properties. Another reason is that those attributes actually have less relationship with their logic implementations. I would reluctant to impose other users to use my libraries with MEF binary files.

The advance of applying Export/Import to property or field values is that I can create one or a list of libraries, where only DI logic is described, nothing else. In addition to that, those libraries may have interfaces to get objects injected from MEF. Those libraries are the only places where I use MEF to define DI mappings and to provide a few of simple interfaces to retrieve objects for clients or applications.

As enough as I have said, I'll show you some examples how to use Export and Import attributes for DI mappings. Let's look at following example. Here are some interface definitions and a controller class definition:

public interface ILog {
  ...
}
...
public interface IDataReader {
  ...
}
...
public class MyController : IProcessController, IDisposable {
  // My implementation class
  ...
  // CTOR
  public MyControler(ILog log, IDataReader reader) {
     ...
  }
  ...
}


Then in a DI mapping library, I define a DIResolver class with some property variables marked as Export or Import:

internal class DIResolver {
  [Export]
  private IProcessController _exportController {
  get {
    return new MyController( _log, _reader );
  }

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

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

}


There are some very interesting points in above codes. First, the class DIResolver can be internal, only visible within the project but not to the outside. It makes sense because I don't need to use this class as an API for any public uses. In addition to that, I can also hide Import and Export properties as private ones if I don't have intension to use them outside the class. MEF can access them.

I define the property _exportController as a mapping relationship between the interface of IProcessController and the implementation class MyContoller. The CTOR of MyController has two parametes: ILog and IDataReader. There are many ways to get instances for those parameters. Here I define two properties as the type of IDataReader and that of ILog, and they are accessible to the CTOR since they are local getters. The key point is that those two properties are also defined as setters and marked by Import attribute. As a result, MEF will be able to inject instances to them. Of course, the Exports for IDataReader and ILog have to be defined some other places such as DI libraries, where I define them in a similar way.

0 comments: