MEF 2.0 - mini series: Part 7 (Catalog filter and Deep hierarchic scoping)
MEF 2.0 - mini series: Part 7 (Catalog filter and Deep hierarchic scoping)
this is the 7th post in the MEF 2.0 mini series.
you can see the following TOC for other posts in this series.
in the previous post I was talking about composition scoping and lifetime management.
on this one, I will extend the composition scoping topic toward hierarchic along with catalog filtering capability.

hierarchic scoping is not trivial, you must understand the hierarchic behavior and what it was design for.
MEF hierarchic was design to enable parts from higher scope to access lower scope's part without re-instantiation, lower part do not target higher scope's part directly.
each hierarchic scope construct by importing a ExportFactory<T> (which we saw in the previous post).
I will try to make it as simple as I can.
the following diagram demonstrate the idea of sub scope that interact with its parent scope's items.

let try to have a similar code structures.
I will use a plug-in base class to trace the instantiations:
Code Snippet
- public abstract class PluginBase
- {
- private static int _globalId = 0;
- protected int _id;
-
- public PluginBase()
- {
- _id = _globalId++; // not thread-safe
- }
- }
derived class's ids will be incremented per instantiation.
the following snippet include the classes which will be used for our hierarchic demonstration:
Code Snippet
- public class Level1ScopeRoot : PluginBase
- {
- [Import]
- public Level1ItemA ItemA { get; private set; }
- [Import]
- public Level1ItemB ItemB { get; private set; }
- }
-
- public class Level1ItemShared : PluginBase { }
-
- public class Level1ItemA : PluginBase
- {
- [Import]
- public Level1ItemShared ItemZ { get; private set; }
- }
-
- public class Level1ItemB : PluginBase
- {
- [Import]
- public Level1ItemShared SharedItem { get; private set; }
-
- [Import]
- public ExportFactory<Level2SubScopeRoot> SubScope { get; private set; }
- }
-
- public class Level2SubScopeRoot : PluginBase
- {
- [Import]
- public Level2SubItem1 SubItem1 { get; private set; }
-
- [Import]
- public Level2SubItem2 SubItem2 { get; private set; }
- }
-
- public class Level2SubItem1 : PluginBase
- {
- [Import]
- public Level1ItemShared ItemShared { get; private set; }
- [Import]
- public Level2SubItemShared SubItemShared { get; private set; }
- }
-
- public class Level2SubItem2 : PluginBase
- {
- [Import]
- public Level2SubItemShared SubItem { get; private set; }
- }
-
- public class Level2SubItemShared : PluginBase { }
all the classes prefixed with the scoping 'Level'.
referring a sub-scope required using of ExportFactory<T> (line 23).
the following diagram is showing their hierarchic and dependencies graph:

our final step is to define a catalog per scope's hierarchic and to set hierarchic structure.
Code Snippet
- var picker = new RegistrationBuilder();
- picker.ForTypesDerivedFrom<PluginBase>()
- // add ScopeLevel metadata
- .AddMetadata("ScopeLevel", t => t.Name.StartsWith("Level1") ? 1 : 2)
- .Export<PluginBase>();
-
- var catalog = new AssemblyCatalog(typeof(Program).Assembly, picker);
-
- var catalogL0 = catalog.Filter(p => p.ContainsPartMetadata("ScopeLevel", 1));
- var catalogL1 = catalog.Filter(p => p.ContainsPartMetadata("ScopeLevel", 2));
-
- var scopeL1 = new CompositionScopeDefinition(catalogL1, null);
- var scopeL0 = new CompositionScopeDefinition(catalogL0, new[] { scopeL1 });
-
- var container = new CompositionContainer(scopeL0);
at line 2-5, I export everything inherits from 'PluginBase' and adding a 'ScopeLevel' metadata with the right scope leveling value.
line 7, define a general assembly catalog (which will be the base for the filtered catalogs).
lines 9,10, are defining a filtered catalog for each scoping level (filtering an existing assembly catalog).
lines 12,13, defines the hierarchic between the scoped catalogs.
line 15, create a container with the top level scope.
now we're ready for a composition.
here you can download a sample code.
Summary
I consider this feature as MEF 2's most complex one.
Hierarchic can become complex, so use it with caution.