DCSIMG
MEF 2.0 - mini series: Part 7 (Catalog filter and Deep hierarchic scoping) - Bnaya Eshet

Bnaya Eshet

Disclaimer

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.

MEF, extension, Composition, ImportMany, Export, Import, .NET 4.5, egistrationBuilder, Catalog

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.

MEF, extension, Composition, ImportMany, Export, Import, .NET 4.5, egistrationBuilder, Catalog

let try to have a similar code structures.

I will use a plug-in base class to trace the instantiations:

Code Snippet
  1. public abstract class PluginBase
  2. {
  3.     private static int _globalId = 0;
  4.     protected int _id;
  5.  
  6.     public PluginBase()
  7.     {
  8.         _id = _globalId++; // not thread-safe
  9.     }
  10. }

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
  1. public class Level1ScopeRoot : PluginBase
  2. {
  3.     [Import]
  4.     public Level1ItemA ItemA { get; private set; }
  5.     [Import]
  6.     public Level1ItemB ItemB { get; private set; }
  7. }
  8.  
  9. public class Level1ItemShared : PluginBase { }
  10.  
  11. public class Level1ItemA : PluginBase
  12. {
  13.     [Import]
  14.     public Level1ItemShared ItemZ { get; private set; }
  15. }
  16.  
  17. public class Level1ItemB : PluginBase
  18. {
  19.     [Import]
  20.     public Level1ItemShared SharedItem { get; private set; }
  21.  
  22.     [Import]
  23.     public ExportFactory<Level2SubScopeRoot> SubScope { get; private set; }
  24. }
  25.  
  26. public class Level2SubScopeRoot : PluginBase
  27. {
  28.     [Import]
  29.     public Level2SubItem1 SubItem1 { get; private set; }
  30.  
  31.     [Import]
  32.     public Level2SubItem2 SubItem2 { get; private set; }
  33. }
  34.  
  35. public class Level2SubItem1 : PluginBase
  36. {
  37.     [Import]
  38.     public Level1ItemShared ItemShared { get; private set; }
  39.     [Import]
  40.     public Level2SubItemShared SubItemShared { get; private set; }
  41. }
  42.  
  43. public class Level2SubItem2 : PluginBase
  44. {
  45.     [Import]
  46.     public Level2SubItemShared SubItem { get; private set; }
  47. }
  48.  
  49. 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:

MEF, extension, Composition, ImportMany, Export, Import, .NET 4.5, egistrationBuilder, Catalog

 

our final step is to define a catalog per scope's hierarchic and to set hierarchic structure.

Code Snippet
  1. var picker = new RegistrationBuilder();
  2. picker.ForTypesDerivedFrom<PluginBase>()
  3.     // add ScopeLevel metadata
  4.     .AddMetadata("ScopeLevel", t => t.Name.StartsWith("Level1") ? 1 : 2)
  5.     .Export<PluginBase>();
  6.  
  7. var catalog = new AssemblyCatalog(typeof(Program).Assembly, picker);
  8.  
  9. var catalogL0 = catalog.Filter(p => p.ContainsPartMetadata("ScopeLevel", 1));
  10. var catalogL1 = catalog.Filter(p => p.ContainsPartMetadata("ScopeLevel", 2));
  11.  
  12. var scopeL1 = new CompositionScopeDefinition(catalogL1, null);
  13. var scopeL0 = new CompositionScopeDefinition(catalogL0, new[] { scopeL1 });
  14.  
  15. 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.


Shout it

Comments

No Comments