MEF for Beginner (Recomposition policy) - part 7
MEF for Beginner (Recomposition policy) - part 7

this is the 7th post of the MEF for Beginner series, the series TOC is available here.
the current post will focus on the recomposition policy.
What is the recomposition policy?
by default the composition should assign the Import only once
(trying to recompose on already composed import will result with exception).
using the recomposition policy we can define that our Import
is allowing reassignment whenever the container is having changes.
What is it good for?
the recomposition can be handy for dynamic data discovery that can
changed during the application lifecycle
(you can put file watcher upon your DirectoryCatalog folder and refresh the catalog
when the change event occurs, this way you can add new plug-in at runtime).
you can also use it to reduce the pressure on your application startup
(by starting with few most common catalog at startup and latter adding the rest).
How to define recompose-able Import / ImportMany?
the following snippet demonstrate the decoration that needed in
order to define recompose-able Import / ImportMany:
Code Snippet
- [ImportMany(AllowRecomposition = true)]
- public string[] Strings { get; set; }
-
- [Import(AllowRecomposition=true)]
- public string Str { get; set; }
as you can see, all it take is to set the AllowRecomposition to true.
Code Sample
the following code sample can be download from here,
it is demonstrating recomposition scenario.
Exported types:
Code Snippet
- internal class ExpMtd1
- {
- [Export]
- public string GetString1 { get { return "GetString 1"; } }
- [Export]
- public string GetString2 { get { return "GetString 2"; } }
- }
- internal class ExpMtd2
- {
- [Export]
- public string GetString3 { get { return "GetString 3"; } }
- [Export]
- public string GetString4 { get { return "GetString 4"; } }
- }
- internal class ExpMtdRuntime
- {
- public ExpMtdRuntime(string s) { GetString = s; }
- [Export]
- public string GetString { get; private set; }
- }
the above code having 3 classes,
each of the 2 first classes ExpMtd1 and ExpMtd2 is simply exporting 2 string.
class ExpMtdRuntime is exporting single string that initialized at the construction time.
looking at the constructor it has no [ImportingConstructor] decoration which mean that
it won't be construct by MEF (we will manually instantiate it at runtime and then we will
introduce the instance to MEF).
Import
Code Snippet
- [ImportMany(AllowRecomposition = true)]
- public string[] Strings { get; set; }
the import is decorated with the AllowRecomposition attribute, which define
that MEF can assign this property upon multiple composition.
Continues composition
the following code will demonstrate multiple compositions,
you can think on scenario like startup plug-ins loading, then latter adding less frequency used plug-ins,
or on demand plug-ins, that maybe adding new plug-ins that was initialize from data source.
Code Snippet
- private static ImpCls s_imp = new ImpCls();
- private static string[] s_beforeState;
-
- static void Main(string[] args)
- {
- var c = new TypeCatalog(typeof(ExpMtd1));
- var catalog = new AggregateCatalog(c);
- var container = new CompositionContainer(catalog);
- container.ComposeParts(s_imp);
- Write(s_imp.Strings); // writing the state before recomposition
-
- container.ExportsChanging += (s, e) => {
- s_beforeState = s_imp.Strings; // snapshot of the value before the recomposition assignment
- };
- container.ExportsChanged += (s, e) => {// getting the changes gap
- var gap = s_imp.Strings.Except(s_beforeState);
- s_beforeState = null;
- Write(gap);
- };
- // adding new catalog (will result in recomposition)
- catalog.Catalogs.Add(new TypeCatalog(typeof(ExpMtd2)));
- for (int i = 0; i < 10; i++)
- { // adding export instance (will result in recomposition)
- container.ComposeParts(new ExpMtdRuntime ("Runtime " + i.ToString()));
- }
- }
-
- private static void Write(IEnumerable<string> strings)
- {
- foreach (var item in strings) { Console.WriteLine(item); }
- }
line 1: s_imp is the instance of the class that having the imports.
line 2: this is temporary variable that we use for storing the state of the collection before the recomposition so
we can get the gap of the changes after the recomposition complete.
lines 6-9: standard MEF catalog initialization, at this stage we only adding the import instance (line 9)
and the ExpMtd1 type (line 6), the catalog does not yet includes the ExpMtd2 and ExpMtdRuntime.
lines 12-18: listening to the changing and changed events, so we will able to write the gap of the
newly discovered parts to the console, whenever the container will recompose.
line 21: adding the ExpMtd2 (by adding new TypeCatalog), it will cause recomposition.
line 22-25: continuously adding ExpMtdRuntime instances, it will cause recomposition for each iteration.
Summary
The recomposition can be very useful in real-life scenarios and it is quite simple to implement.