Saturday, April 10, 2010

MEF and IoC/DI (5)

Let's look my second API class - DICatalogBase.

DICatalogBase Class


The second class I use in my practice of DI library is the DICatalog class. The main purpose of this class is to provide APIs load DIs as parts to MEF and to pass information to customize Export instances. DICatalog is a customized API class for public usage. Therefore, it is an optional class if the customization is required.

Add Parts


To facilitate loading DI parts to MEF, a base class of DICatalogBase with several CTORs (constructors) is provided, which correspond to load one or a list of libraries as assembly catalog, directory catalog, type catalog and aggregate catalogs.

Here are some examples. First one is to load DI by an executing assembly. In the base class the assembly is converted to assembly catalog instance to MEF:
public class DICatalog : DICatalogBase {
  public DICatalog():
    base(Assembly.GetExecutingAssembly())))
  {...}
}

internal class DIResolver : DIResolverBase {
    // Define Import and Export properties
  ...
}


The following is another example to use a base CTOR with a collection of two DICatalogBase instances. The first one is load the executing assembly as DI part and the second to load all the DI parts in a directory ("DIExtensions" under the executing assembly location).
public class DICatalog : DICatalogBase {
  private const string DIExtensions = "DIExtensions";
  public DICatalog()
    : base(new DICatalogBase[] {
      new DICatalogBase(Assembly.GetExecutingAssembly()),
      new DICatalogBase(DIExtensions)
      })
  {...}
}

internal class DIResolver : DIResolverBase {
    // Define Import and Export properties
  ...
}

Since the inherited instances based on DICatalogBase are created by clients and then loaded to MEF; therefore, the inherited classes have to be public.

Pass Customized Information


The customized information is passed through a customized CTOR of DICatalog class. For example, if some exported instances have to be created based on a configuration file, a customized CTOR can be used to input the configuration information.

Here is an example of DICatalog class with a Configuration class as CTOR's parameter:
public class DICatalog : DICatalogBase {
  [Export]
  private static LogConfig _logConfig { get; set; }

  internal static DataReaderConfig _readerConfig { get; private set; }

  public DICatalog(Configuration config)
    : base(Assembly.GetExecutingAssembly()) {
    _logConfig = config.LogConfig;
    _readerConfig = config.ReaderConfig;
    ...}
}

internal class DIResolver : DIResolverBase {
    // Define Import and Export properties
  ...
}

The configuration is passed as CTOR's parameter to the DICatalog class. How is this information passed to MEF's parts? Here I use two ways to achieve it.

The first one is by using Export attribute for a private property value, i.e., to export LogConfig. The DICatalog class has a customized CTOR, therefore MEF cannot create an instance of it. Even MEF could create an instance of the class if the default CTOR were defined; we would not be able to set the property value for the instance. In order to set the Export property value, the property has to be static and it can be set in CTOR. Fortunately, MEF does pick up the exported value from a static property. Here LogConfig is an exported property known to MEF.

The advantage of using Export attribute is that the exported value can be injected to anywhere in other DI libraries, components, or application through MEF Import attribute. If a piece of information is only used within a library, by using static property may be good enough. Here the second property of DataReaderConfig may be used within the DIResolver class, to customize a DataReader class instance as Export.

0 comments: