Defining Scope in MEF with CompositionScopeDefinition
Asked Answered
J

1

10

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.

Jetblack answered 5/6, 2013 at 14:54 Comment(0)
J
2

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.

Jetblack answered 24/6, 2013 at 12:3 Comment(2)
How do I fit this with Prism Bootstrapper?Tallulah
Is there a reason why the CompositionScopeDefinition objects aren't being disposed?Devout

© 2022 - 2024 — McMap. All rights reserved.