DCSIMG
MEF disguising to Castle Windsor - Bnaya Eshet

Bnaya Eshet

Disclaimer

MEF disguising to Castle Windsor

This post explore advance techniques of changing the default MEF discovery behavior

into Castle Windsor like behavior (Castle Windsor is used as test case and it’s not the main subject of this post).

 

The code for this post available here (it built upon MEF preview 5.)

 

Prerequisites

This post assume that you have some knowledge on MEF  and some understanding on DI (dependency injection)

Previous knowledge on Castle Windsor (which is one of the most common DI implementation) is not needed.

you can read more on MEF at the following posts: MEF Introduction, http://delicious.com/bnaya/mef

 

Background

Out of the box, MEF come with a few very useful discovery model that relay on attribute decoration.

those model includes TypeCatalog, AssemblyCatalog, and DirectoryCatalog.

 

Brief description about MEF architecture:

MEF layers

The MEF architecture has 3 layers of abstraction:

  • Hosting responsible for the actual composition and the actual API for the MEF consumers
  • Primitive is the abstraction of the MEF components but dose not implement
    the actual discovery and matching model
  • Model layer responsible for the actual discovery and matching model,
    MEF come with attribute model out of the box

image

 

Custom model

Creating custom module involve with inheritance of the following classes:

  • Catalog which responsible for discovery and matching strategy
  • ComposablePartDefinition which holding imports and exports definition
    and use as factory for ComposablePart
  • ExportDefinition holding the information about the export parts
  • ImportDefinition define constraint which filler the ExportDefinition that satisfy the import
  • ComposablePart which is responsible for instantiating the concrete export part

 

 

image

 

About the code sample

The code sample taking Castle Windsor model as test case for custom MEF model.

The sample solution has 2 projects:

  • CastleMEF project which implement the MEF model
  • CastleConsole which consume the CastleMEF module, using Castle Windsor configuration

 

CastleConsole configuration

  <castle>
    <components>
      <component id="Logger"
               service="Bnaya.Samples.ILogger, CastleConsole"
               type="Bnaya.Samples.ColsoleLogger, CastleConsole">
      </component>
    </components>
  </castle>

We can see very simple Castle Windsor configuration that map ILogger to be instantiate with ConsoleLogger

 

Castle Windsor instantiation Vs. MEF instantiation

Instantiating ILogger using Castle Windsor:

IWindsorContainer castleContainer = new WindsorContainer (new XmlInterpreter ());
ILogger castleLogger = castleContainer.Resolve<ILogger> ();
 
Instantiating ILogger using MEF:
ILogger mefSimpleLogger = CastelCompositor.Resolve<ILogger> ();
As we can see the consumer get quite similar experience. 

 

Diving into the custom MEF implementation

Catalog:

public class CastelCatalog: ComposablePartCatalog {
    private IQueryable<ComposablePartDefinition> _queryableParts = null;

    public override IQueryable<ComposablePartDefinition> Parts {
        get {
            var collection = new List<ComposablePartDefinition> ();
            collection.Add (new CastelPartDefinition (…));
            return this._queryableParts;
        }
    }
}

The catalog responsible for give IQueryable of ComposablePartDefinition

 

ComposablePartDefinition

public class CastelPartDefinition: ComposablePartDefinition {
    private Dictionary<string, object> _mapping; // the mapping as the catalog resolve from the configuration

    public CastelPartDefinition ( Dictionary<string, object> mapping ) {
        _mapping = mapping;
    }

    public override ComposablePart CreatePart () {
        return new CastelPart (this);
    }

    public override IEnumerable<ExportDefinition> ExportDefinitions {
        get {
            return from item in _mapping
                select new CastelExportDefinition (item.Key, item.Value) as ExportDefinition;
        }
    }

    public override IEnumerable<ImportDefinition> ImportDefinitions {
        get {
            return from item in _mapping
                         select new CastelImportDefinition (item.Key) as ImportDefinition;
        }
    }
}

 

ExportDefinition

public class CastelExportDefinition: ExportDefinition {
    private IDictionary<string, object> _metadata; 

    public CastelExportDefinition ( string contractName, object instance )
        : base (contractName, null) {
        _metadata = new Dictionary<string, object> (base.Metadata);
        _metadata.Add ("Instance", instance); // holding the actual instance
        _metadata.Add ("ExportTypeIdentity", contractName); // won't work without it
    }

    public override IDictionary<string, object> Metadata {
        get {
            return _metadata;
        }
    }
}

Export definition responsible to supply the right instance information to the right contract

 

ImportDefinition

public class CastelImportDefinition: ImportDefinition {
    public CastelImportDefinition ( string contract )
        : base (e => e.ContractName == contract, 
                    ImportCardinality.ExactlyOne, false, false) {
    }
}

Import definition responsible to set the filtering which satisfy the import (e => e.ContractName == contract)

 

ComposablePart

public class CastelPart: ComposablePart {
    private readonly CastelPartDefinition _def;

    public CastelPart (CastelPartDefinition def) {
        _def = def;
    }
    public override object GetExportedObject ( ExportDefinition definition ){
        if (!definition.Metadata.ContainsKey ("Instance"))
            return null;
        
        return definition.Metadata["Instance"];
    }
}

The role of the composable part is to hand the concrete instance

 

Summary

MEF built upon abstraction layer (primitives) therefore we can replace the

out of the box attributed model with our own.

It can be useful in cases that we need to compose the MEF upon
runtime information like central configuration, cases where the
design-time attribute model does not fit.

 

 
Posted: Jun 13 2009, 03:29 PM by bnaya
תגים:,

Comments

No Comments