Tuesday, November 19, 2013

MEF and deciding which export instance to use

Having used MEF in the past, I was really keen to use more extensively in a up coming project, which will involve extending 'core' functionality with customer extensions (i.e. overriding) . I'm enjoying the simplicity of MEF in regards to resolving dependencies, and the lack of configuration required / setup code when comparing with IOC containers. With the amount of customers extensions that will be implemented eventually, the amount of configuration required to wire these up won't scale (like it didn't for the previous version of the product where Unity was used).

So essentially, if I drop an assembly into the bin folder with customer extensions on base functionality, then the derived Exports would be used over the 'base' Exports. If the customer extension is not present, (i.e. just the base Export is), then just use this one.

In code this would look like:

public class CoreApplicationQueryService : IApplicationQueryService

public class CustomerApplicationQueryService : CoreApplicationQueryService

If CustomerApplicationQueryService is present, use that, if not, default to CoreApplicationQueryService.

[Import] won't suffice because there will be multiple Exports that match if the Customer version is present, therefore an exception will be thrown. Therefore [ImportMany] will have to be used. But, once the multiple Exports have been picked up, I need a way of deciding which instance to use. That is where [ExportMetadata] comes in.

I've used [ExportMetadata] to indicate is the Export is defined as an (Customer) extension of not:

[ExportMetadata("Extension", false)]
public class CoreApplicationQueryService : IApplicationQueryService

[ExportMetadata("Extension", true)]
public class CustomerApplicationQueryService : CoreApplicationQueryService

... where true ("Extension", true) indicates that this instance is a extension instance.

There is a little bit of magic where you then need to create an interface to match the parameters in the ExportMetadata attribute - e.g:

public interface IExportMetaData
{
bool Extension { get; }
}

The next step is to import the parts, e.g. set this property. IExportMetaData is part of the property definition as part of the Lazy type:

[ImportMany(typeof(IApplicationQueryService))]
public IEnumerable<Lazy<IApplicationQueryService, IExportMetaData>> ApplicationQueryServices { get; set; }

Next compose the parts, and then cycle through the instances resolved to find the extended instance (if there). An example is below:

var directoryCatalog = new DirectoryCatalog("bin");
var compositionContainer = new CompositionContainer(directoryCatalog);
compositionContainer.ComposeParts(this);
foreach (var item in ApplicationQueryServices)
{
   if(item.Metadata.Extension)
{
var message = item.Value.GenerateMessage();
}
}

No comments:

Post a Comment