One of the coolest thing in the newest version of MEF is scoping support. Not because I’ve participated in designing and eventually patented some aspects of it – not at all – but because it’s actually quite helpful.
Scopes, as you can imagine, allow you to define isolated “bubbles” of composition.
On the components side, we use an ExportFactory
One thing that is particularly handy in IoC containers is supporting default services, but allow for overrides. Windsor even use this taxonomy. The following is a trick that uses scopes to achieve the same behavior.
Before saying that it’s easier, simpler, faster to achieve that using Container X, Y or Z, remember that MEF is all about federated systems, and that comes with challenges and constraints.
Here’s the trick: I define three scopes: Default, Overrides and App. All default services are bound to the Default scope. The app level components live in the App scope. In the middle, the Override scopes will be used to replace default services.
using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; class Program{ static void Main(string[] args){ var ubber = new AssemblyCatalog(typeof (Program).Assembly);var defLevelCat = ubber.Filter(cpd => cpd.ContainsPartMetadata("Scope", "Default"));var overLevelCat = defLevelCat.Complement.Filter(cpd => cpd.ContainsPartMetadata("Scope", "Override"));var appLevelCat = overLevelCat.Complement.Filter(cpd => cpd.ContainsPartMetadata("Scope", "App")); var appDef = new CompositionScopeDefinition(appLevelCat, new CompositionScopeDefinition[0], appLevelCat.Parts.SelectMany(p => p.ExportDefinitions));var overDef = new CompositionScopeDefinition(overLevelCat, new [] { appDef }, overLevelCat.Parts.SelectMany(p => p.ExportDefinitions));var defDef = new CompositionScopeDefinition(defLevelCat, new[] { overDef }, defLevelCat.Parts.SelectMany(p => p.ExportDefinitions)); var cont = new CompositionContainer(defDef); var entryPoint = cont.GetExportedValue<EntryPoint>(); var sndLevel = entryPoint.Creator.CreateExport().Value; var lastLevel = sndLevel.Creator.CreateExport().Value; var component = lastLevel.com; } } [Export][PartMetadata("Scope", "Default")] public class EntryPoint{ [Import(RequiredCreationPolicy = CreationPolicy.NewScope, AllowDefault = true)]public ExportFactory<OverrideLevelScopeCreator> Creator { get; set; } } [Export][PartMetadata("Scope", "Override")] public class OverrideLevelScopeCreator{ [Import(RequiredCreationPolicy = CreationPolicy.NewScope, AllowDefault = true)]public ExportFactory<AppLevelScopeCreator> Creator { get; set; } } [Export][PartMetadata("Scope", "App")] public class AppLevelScopeCreator{ [Import]public Component com { get; set; } } public interface IService{ } [Export(typeof(IService))][PartMetadata("Scope", "Default")] public class DefaultService : IService{ } [Export(typeof(IService))][PartMetadata("Scope", "Override")] public class OverrideService : IService{ [Import(Source = ImportSource.NonLocal)]public IService ServParent { get; set; } }[Export, PartMetadata("Scope", "App")] public class Component{ [Import]public IService Serv { get; set; } }
Have fun!