Using MEF scopes to support service overrides

December 28th, 2011

Warning: file(http://svn.wp-plugins.org/devformatter/branches/langs/cs.php): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in C:\websites\castle\blogs\hammett_castleblog\wp-content\plugins\devformatter\devgeshi.php on line 100 Warning: implode(): Invalid arguments passed in C:\websites\castle\blogs\hammett_castleblog\wp-content\plugins\devformatter\devgeshi.php on line 100 Warning: file(http://svn.wp-plugins.org/devformatter/branches/langs/cs.php): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in C:\websites\castle\blogs\hammett_castleblog\wp-content\plugins\devformatter\devgeshi.php on line 100 Warning: implode(): Invalid arguments passed in C:\websites\castle\blogs\hammett_castleblog\wp-content\plugins\devformatter\devgeshi.php on line 100 Warning: file(http://svn.wp-plugins.org/devformatter/branches/langs/cs.php): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in C:\websites\castle\blogs\hammett_castleblog\wp-content\plugins\devformatter\devgeshi.php on line 100 Warning: implode(): Invalid arguments passed in C:\websites\castle\blogs\hammett_castleblog\wp-content\plugins\devformatter\devgeshi.php on line 100

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 (should have been ScopeFactory, but dont get me started on this one) with CreationPolicy.NewScope (ughh) to achieve this behavior. On the hosting side, you need to define an hierarchy of CompositionScopeDefinition and give it to the container.

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.

 cs |  copy code |? 
01

02
    using System.ComponentModel.Composition;
03
    using System.ComponentModel.Composition.Hosting;
04
05
    class Program
06
    {
07
        static void Main(string[] args)
08
        {
09
            var ubber = new AssemblyCatalog(typeof (Program).Assembly);
10
11
            var defLevelCat = ubber.Filter(cpd => cpd.ContainsPartMetadata("Scope", "Default"));
12
            var overLevelCat = defLevelCat.Complement.Filter(cpd => cpd.ContainsPartMetadata("Scope", "Override"));
13
            var appLevelCat = overLevelCat.Complement.Filter(cpd => cpd.ContainsPartMetadata("Scope", "App"));
14
15
            var appDef = new CompositionScopeDefinition(appLevelCat, new CompositionScopeDefinition[0],
16
                                                        appLevelCat.Parts.SelectMany(p => p.ExportDefinitions));
17
            var overDef = new CompositionScopeDefinition(overLevelCat, new [] { appDef },
18
                                                         overLevelCat.Parts.SelectMany(p => p.ExportDefinitions));
19
            var defDef = new CompositionScopeDefinition(defLevelCat, new[] { overDef },
20
                                                        defLevelCat.Parts.SelectMany(p => p.ExportDefinitions));
21
22
            var cont = new CompositionContainer(defDef);
23
24
            var entryPoint = cont.GetExportedValue<EntryPoint>();
25
            var sndLevel = entryPoint.Creator.CreateExport().Value;
26
            var lastLevel = sndLevel.Creator.CreateExport().Value;
27
            var component = lastLevel.com;
28
29
        }
30
    }
31
32
    [Export]
33
    [PartMetadata("Scope", "Default")]
34
    public class EntryPoint
35
    {
36
        [Import(RequiredCreationPolicy = CreationPolicy.NewScope, AllowDefault = true)]
37
        public ExportFactory<OverrideLevelScopeCreator> Creator { get; set; }
38
    }
39
40
    [Export]
41
    [PartMetadata("Scope", "Override")]
42
    public class OverrideLevelScopeCreator
43
    {
44
        [Import(RequiredCreationPolicy = CreationPolicy.NewScope, AllowDefault = true)]
45
        public ExportFactory<AppLevelScopeCreator> Creator { get; set; }        
46
    }
47
48
    [Export]
49
    [PartMetadata("Scope", "App")]
50
    public class AppLevelScopeCreator
51
    {
52
        [Import]
53
        public Component com { get; set; }
54
    }
55
56
    public interface IService
57
    {
58
    }
59
60
    [Export(typeof(IService))]
61
    [PartMetadata("Scope", "Default")]
62
    public class DefaultService : IService
63
    {
64
    }
65
66
    [Export(typeof(IService))]
67
    [PartMetadata("Scope", "Override")]
68
    public class OverrideService : IService
69
    {
70
        [Import(Source = ImportSource.NonLocal)]
71
        public IService ServParent { get; set; }
72
    }
73
    
74
    [Export, PartMetadata("Scope", "App")]
75
    public class Component
76
    {
77
        [Import]
78
        public IService Serv { get; set; }
79
    }
80

Have fun!

Categories: .NET, MEF | Top Of Page | 2 Comments » |

2 Responses to “Using MEF scopes to support service overrides”

Daniel Plaisted Says:

I think we got rid of or are going to get rid of CreationPolicy.NewScope. It wasn’t really doing anything, since the scoping is set up via CompositionScopeDefinitions. You just use an ExportFactory and it will have access to exports on the public surface of child scopes to the one you’re in.

It looks like parts which don’t have any scope metadata won’t be included at all. I’d probably put them in the app level catalog.

Also, the greater than and less than symbols for generics are causing parts of your code not to show up.

Daniel

hammett Says:

Is the removal of .NewScope something that didnt make into the latest codeplex bits?

Leave a Reply