MEF: Dependencies are Queries?

Start to schematically represent any component system and you’re likely to come up with a diagram like:


So what do the dependency and the service actually mean?

In most implementations, each is associated with a key:


Here the Screen Renderer component needs the Typesetter service, which the Fast Typesetter component can provide.

Dependency resolution is a done by matching keys – resolving the Screen Renderer’s dependency boils down to doing a dictionary lookup with Typesetter as the key, to find the Fast Typesetter implementation.

This is the case whether the dependencies are expressed as strings, types, or some combination of the two.

While this model is very powerful and can be taken a long way, ‘open’ systems can benefit from an alternative approach.

Imagine a system in which multiple renderers and typesetters exist, each with distinct capabilities:


The dependencies above are no longer evaluated using simple equality comparisons.

On the right, the capabilities of each component are represented as structured data: the service provided, plus a collection of key-value pairs bearing more detailed information. Together these are a service definition.

On the left, the dependencies of each component are represented as predicates over the possible service definitions. The predicate evaluates to true if the service definition can satisfy the dependencies.

So, the Print Renderer’s requirements, { Quality == High }, are true when evaluated against the Accurate Typesetter’s service definition, and false when executed against the Fast Typesetter’s.

In effect, components express their dependencies as queries over the entire set of available services.

To get an idea of where the power of this technique comes from, consider the way the example will compose: the Screen Renderer gets a fast Typesetter and the Print Renderer gets an accurate one. In a system with a better Typesetter – one that is both fast and accurate – a single Typesetter implementation could fulfill both dependencies.

Constraints aren’t just limited to equality comparisons either – if speed is expressed in characters per second, the Screen Renderer could specify { Speed > 500 }.

If you haven’t guessed already, this is the model that the MEF Primitives use. The service definitions are exposed with the type ExportDefinition. The predicate for testing ExportDefinitions for suitability is encoded in ImportDefinition.Constraint.


One of the constraints in the example might be expressed as:


The system is elegant, but there are a few things to be aware of. The first that comes to mind is how the constraint is Boolean, so ‘falling back’ to a slow Typesetter when no fast one is available means expressing two separate constraints

Because ImportDefinition.Constraint is a Linq expression, and thanks to some special-case logic, MEF is able to resolve many dependencies without doing the linear scan that the query implies. This is only the case when constraints take the typical forms expressible in MEF’s Attributed Programming Model. Arbitrary constraints can result in a linear scan (although the way is clear to optimizing these in the future.)

One other interesting note – it is technically possible to transform an import constraint into a format that could be evaluated by a database-backed Linq provider… If for some reason you’d like to do that :)

You might be wondering why code snippets like the predicate above don’t appear in the MEF examples you typically see. The answer is that this is the key to understanding programming models. A programming model, in the MEF sense, translates some easy-to-type format like the Import and Export attributes, into constraints under-the-hood.

Constraint-based dependency resolution is one of the foundations that future versions of MEF will build upon.

Comments (7)

  1. Thank you for submitting this cool story – Trackback from DotNetShoutout

  2. Bill Pierce says:

    Doesn’t this make service consumers more tightly coupled with service providers?  If I need a fast typesetter why wouldn’t I resolve an IFastTypeSetter?  Or add RenderFast and RenderAccurate methods to ITypeSetter?

    I think more concrete examples would aid the discussion.

  3. niblumha says:

    Hi Bill!

    I’m not sure there is any more coupling – just a tighter contract.

    Examples that fit on a page are hard to come by…

    Compilers in an IDE may be another illustrative example. ICompiler is annotated with metadata describing the language that can be compiled. A build tool will want a compiler for a specific language, while a configuration tool may wish to list all of the available compilers.

    This is one of a set of scenarios that the techniques you described are harder to adapt to – using contracts with metadata is more flexible than creating interfaces for every specialisation, or methods for every combination of capabilities.

    Hope this illustrates the difference a little better – please keep the questions coming.


  4. With the Managed Extensibility Framework (MEF), you can use Import and Export attributes to declare what

  5. David Barrett says:

    Does this work today in MEF Preview 5?  I mean, is it possible to provide this sort of constraint?  I’m trying desperately to filter a list of exports that satisfy an import based on a piece of metadata but can’t seem to get it to work.

  6. niblumha says:

    Hi David,

    The architecture supports it – you can construct an instance of ImportDefinition with any arbitrary constraint you require (there are some perf caveats to this.)

    There is, however, no high-level support in the attributed programming model (e.g. via the [Import] attribute) so in your case the best option is to use:


    Export<IFoo, IFooMetadata>[] services;

    …and then select from the services as required:

    var servicesForX = services.Where(svc => svc.MetadataView.Purpose == "X");

    Lazy instantiation means that you’ll only instantiate the service instance you actually use.

    In the future we hope to simplify this scenario. You can consider this post a peek under the hood and a nod to the kinds of things we may build in the future.

    Hope this helps!


  7. Gotcha.  So I wan’t being an idiot when I couldn’t find the attribute overload to provide this out of the box?  :)

    Thanks so much for the reply, Nick.  I’ll just use IQueryable to get the items I want/need.  I look forward to seeing how MEF matures and develops.  Neat stuff so far!