Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining Scope in MEF with CompositionScopeDefinition

At the root of my application I have an AggregateCatalog and a CompositionContainer like so:

AggregateCatalog aggregateCatalog = new AggregateCatalog();
CompositionContainer compositionContainer = new CompositionContainer(aggregateCatalog);

My application loads up modules which contain several exports as shown in the diagram below. I want to use CompositionScopeDefinition to scope the exports circled in the diagram.

Module Export Diagram

See here for the class definitions.

// Create CompositionScopeDefinition.
TypeCatalog globalParts = new TypeCatalog(typeof(RequestListener));
TypeCatalog scopedParts = new TypeCatalog(typeof(RequestHandler), typeof(DataAccessLayer), typeof(Logger), typeof(DatabaseConnection));
CompositionScopeDefinition compositionScopeDefinition = new CompositionScopeDefinition(
    globalParts,
    new[] { new CompositionScopeDefinition(scopedParts, null) });

// Register CompositionScopeDefinition.
aggregateCatalog.Catalogs.Add(compositionScopeDefinition);

// Create an instance of RequestListener.
RequestListener requestListener = compositionContainer.GetExportedValue<RequestListener>();

However, this causes the following exception:

System.ComponentModel.Composition.ImportCardinalityMismatchException occurred Message=No exports were found that match the constraint: ContractName MyNamespace.RequestListener RequiredTypeIdentity MyNamespace.RequestListener InnerException:

How can add my scoped exports using CompositionScopeDefinition to an existing AggregateCatalog and initialise them using my existing CompositionContainer?

Update

It seems that the problem using an AggregateCatalog. If I add the CompositionScopeDefinition to the CompositionContainer directly everything works but this stops me from adding other catalogs to the CompositionContainer.

like image 641
Muhammad Rehan Saeed Avatar asked Jun 05 '13 14:06

Muhammad Rehan Saeed


1 Answers

I spoke to the guys who work on MEF on CodePlex. This was essentially their answer:

// Handy extension methods for dealing with CompositionScopeDefinition (Not relevant to this answer but useful).
public static class ComposablePartCatalogExtensions
{
    public static CompositionScopeDefinition AsScope(this ComposablePartCatalog catalog, params CompositionScopeDefinition[] children)
    {
        return new CompositionScopeDefinition(catalog, children);
    }

    public static CompositionScopeDefinition AsScopeWithPublicSurface<T>(this ComposablePartCatalog catalog, params CompositionScopeDefinition[] children)
    {
        IEnumerable<ExportDefinition> definitions = catalog.Parts.SelectMany((p) => p.ExportDefinitions.Where((e) => e.ContractName == AttributedModelServices.GetContractName(typeof(T))));
        return new CompositionScopeDefinition(catalog, children, definitions);
    }
}

AggregateCatalog aggregateCatalog = new AggregateCatalog();
AggregateCatalog childAggregateCatalog = new AggregateCatalog();
CompositionScopeDefinition compositionScopeDefinition = aggregateCatalog.AsScope(childAggregateCatalog.AsScope());
CompositionContainer compositionContainer = new CompositionContainer(compositionScopeDefinition);

TypeCatalog globalParts = new TypeCatalog(typeof(RequestListener));
TypeCatalog scopedParts = new TypeCatalog(typeof(RequestHandler), typeof(DataAccessLayer), typeof(Logger), typeof(DatabaseConnection));

aggregateCatalog.Catalogs.Add(globalParts);
childAggregateCatalog.Catalogs.Add(scopedParts);

RequestListener requestListener = compositionContainer.GetExportedValue<RequestListener>();

Essentially you can't place a CompositionScopeDefinition inside an AggregateCatalog. So you can invert the relationship and have a CompositionScopeDefinition at the root level and multiple AggregateCatalog's for each scope level you are trying to represent. This seems to work great. You also get the added benefit of having a single CompositionContainer.

like image 76
Muhammad Rehan Saeed Avatar answered Oct 05 '22 13:10

Muhammad Rehan Saeed