Chapter 15. MEF

Menu

  • Introducing MEF
  • Managing lifetime
  • Working with multiple components
  • Configuring difficult APIs

In the previous five chapters, you saw how various DI CONTAINERS can be used as tools to implement the patterns and practices laid out in the rest of the book. In this chapter, we’re going to do something slightly different, because the Managed Extensibility Framework (MEF) isn’t really a DI CONTAINER.

As its name implies, MEF is a framework that addresses extensibility concerns for applications. The focus is on enabling add-in scenarios for standard software. Visual Studio 2010 is probably the first and most prominent application that uses MEF to support plug-ins, but any application built on .NET 4 or Silverlight 4 can use it to expose extensibility features.

If MEF isn’t a DI CONTAINER, then why use an entire chapter covering it in this book? The most important reason is that MEF looks so much like a DI CONTAINER that you need to spend some time with it to understand the differences between it and real DI CONTAINERS. Because it’s part of .NET 4 and Silverlight 4, it may be tempting to use it as a DI CONTAINER if you don’t understand the subtle differences. The purpose of this chapter is to exhibit these differences so you can make an informed decision.

 

Note

Please keep in mind that you can always skip this chapter if MEF doesn’t interest you and you’ve already decided to use another DI CONTAINER.

 

 

Is MEF a DI Container?

There’s a lot of confusion about whether or not MEF is a DI CONTAINER. The short answer is that it isn’t, but that it shares so many traits with “proper” DI CONTAINERS that it may become a full-fledged DI CONTAINER in the future.

MEF was built for a different purpose than a DI CONTAINER. Its purpose is to provide a common framework for enabling add-in functionality for standard applications. From the perspective of a standard application, an add-in is an unknown component. Whereas the add-in is most likely required to expose a certain interface, this is about all the application knows about it. There may be zero, one, or a lot of add-ins, depending on the environment. This is different from a DI CONTAINER, where we typically know about all (or most of) the components at compile time.

When we use a DI CONTAINER as a tool to compose an application, we know about the components that make up the application, and we use this knowledge to configure the container in the application’s COMPOSITION ROOT.

On the other hand, when it comes to plug-ins, we only know that the plug-ins must implement some sort of ABSTRACTION, but we can’t compile the application with a configuration of specific plug-ins, because they are unknown at design time. Instead, we need a discoverability mechanism.

A traditional discoverability mechanism for add-ins is to scan a certain folder for assemblies to find all classes implementing the required ABSTRACTION. However, this doesn’t address the issue that may occur when the add-in itself has DEPENDENCIES. MEF, on the other hand, addresses exactly this scenario through its advanced discovery model that uses attributes to define consumers and their services.

A DI CONTAINER favors decoupled composition of services. This provides the greatest degree of flexibility, but comes at a cost: as developers, we must have knowledge about the components we wish to compose at the time we configure the container.

MEF favors discovery of components. This successfully addresses the issue when we know little about the add-ins at design time. The tradeoff is that the discovery mechanism is tightly coupled with the components, so we lose some flexibility.

When we consider the internal architecture of MEF, it turns out that discovery and composition are decoupled. This means that it’s possible for Microsoft to evolve MEF in the direction of a true DI CONTAINER.[1] On the other hand, some DI CONTAINERS offer such powerful convention-based features that they may encroach on MEF in the future.

1 For more information, see Glenn Block, “Should I use MEF for my general IoC needs?” 2009, http://blogs.msdn.com/b/gblock/archive/2009/08/16/should-i-use-mef-for-my-general-ioc-needs.aspx

Even today, MEF shares so many similarities with DI CONTAINERS that some of its creators already view it as one, while others don’t.

 

 

Tip

If you’ve already attempted to use MEF as a DI CONTAINER and have been left bewildered and disappointed, this chapter should explain why.

 

Although being a dedicated DI CONTAINER was not the first priority when MEF was conceived and designed, it turns out that you can use it as one. At times it’s somewhat awkward, but still possible. There are some scenarios where it makes sense to use MEF as a DI CONTAINER—particularly in applications that already use it to implement extensibility features.

 

Note

The whole premise of this chapter is (inadvertently) setting MEF up to fail. This doesn’t mean that MEF isn’t good at what it does; it means that we’re trying to make it do something for which it isn’t designed. We’re trying to fit a square peg in a round hole.

 

In this chapter, we’ll examine how MEF can be used to apply the principles and patterns covered in parts 13. Figure 15.1 shows the structure of the chapter.

Figure 15.1. This chapter is divided into four sections. The first section provides an overall introduction to MEF and demonstrates how to configure and resolve components. The next three sections each deal with usage patterns that require a bit of extra attention; you can read them all in order, or you can skip some and read only the ones that interest you.

The structure of the chapter mirrors the structure of all the other chapters in part 4, because I believe that this makes it easier to compare the different DI CONTAINERS. In the case of MEF, this results in four sections of rather unequal size and importance. We’ll spend a lot of time in the introduction to get a feel for MEF and its API, but then cover LIFETIME MANAGEMENT in only a few pages, because MEF doesn’t have many options in that area. The topic of multiple components then takes up most of the rest of the chapter, with the last section again being rather short. With the introduction as a prerequisite, each of the three other sections can be read independently of each other.

This chapter should enable you to get started, as well as deal with the most common issues that may come up as you use MEF. However, this is by no way a fair or comprehensive treatment of MEF because we evaluate it in terms of a DI CONTAINER,[2] instead of its real purpose.

2 For a more idiomatic introduction to MEF, see Glenn Block, “Managed Extensibility Framework: Building Composable Apps in .NET 4 with the Managed Extensibility Framework,” MSDN Magazine (February 2010). Also available online at http://msdn.microsoft.com/en-us/magazine/ee291628.aspx

You can read the chapter in isolation from the rest of part 4 specifically to learn about MEF, or you can read it together with the other chapters in part 4 to compare it with “real” DI CONTAINERS. The focus of this chapter is to show how MEF relates to and implements the patterns and principles described in parts 13.

15.1. Introducing MEF

In this section, you’ll learn where to get MEF, what you get, and how you start using it. We’ll also look at how components are configured and packaged. Table 15.1 provides fundamental information that you’re likely to need to get started.

Table 15.1. MEF at a glance

Question

Answer

Where do I get it? MEF is part of .NET 4 and Silverlight 4.
What’s in the download? You get MEF when you install .NET 4 or Silverlight 4. It’s part of the Base Class Library and packaged in the System.ComponentModel.Composition assembly. If you visit http://mef.codeplex.com/, you can also download the source code to peruse.
Which platforms are supported? .NET 4 and Silverlight 4. On http://mef.codeplex.com/ you can also find unsupported versions for .NET 3.5 SP1 and Silverlight 3.
How much does it cost? Nothing. It’s part of .NET 4 and Silverlight 4.
Where can I get help? Because MEF is part of .NET and Silverlight, you can get support from Microsoft.
On which version is this chapter based? .NET 4.

In contrast with other DI CONTAINERS, MEF has a different rhythm of usage. We never configure a container, but rather annotate the components themselves with attributes. Figure 15.2 shows the relationship between components and the composition engine itself.

Figure 15.2. With MEF, we annotate parts (for example, classes and members) with attributes in a separate work phase. When we compose an application, we first select the appropriate parts into a catalog and then use the catalog to define a container from which we can resolve components.

 

MEF terminology

MEF uses terminology that’s a bit different than what you’re used to when we talk about DI CONTAINERS.

We normally call collaborating classes components, but in MEF the nearest term is part. A part is a class or member that provides or consumes DEPENDENCIES.

When a part consumes a DEPENDENCY, we say that it imports it. Conversely, when it provides a service, it exports it. In a classic case of jargon-fueled grammatical mayhem, both import and export can also be used as nouns.

Exports and imports are defined by annotating parts with attributes.

When we compose applications, we match exports with imports according to contracts. We often use types (such as interfaces) as contracts, but MEF is more flexible than that; a contract is really just a string.

 

With other DI CONTAINERS, we use a decoupled configuration API to define which components are available, how concrete types map to ABSTRACTIONS, how components are created, and whether instances are shared or not.

Conversely, with MEF, we bundle that information with each part by applying attributes on types and members. This is easy to understand, but tightly couples the configuration of the component to the component itself.

 

Note

Keep in mind that MEF in version 1 uses attributes as the default (and only) method of discovery, but at the core, isn’t at all coupled to attributes as a means of discovery.

 

To compose an application, we select appropriate parts and package them into a catalog, and then create a container that can resolve components from that catalog.

When we’re done with this section, you should have a good feeling for the overall usage pattern of MEF, and you should be able to start using it in well-behaved scenarios where all parts define simple imports and exports. We’ll start with the simplest scenario and see how you can resolve objects using a MEF container.

15.1.1. Resolving objects

The core service of any DI CONTAINER is to compose and resolve components. In this section, we’ll look at the API that enables us to resolve components with MEF. As with any other container, resolving objects is as simple as invoking a simple method, but with MEF, we can’t resolve anything until the required exports are available.

If you recall the discussion about resolving components with Castle Windsor, you may remember that Windsor requires us to register all relevant components before we can resolve them. MEF has an analogous requirement, although there’s no way we can register a component; instead, a part must export the desired service.

To resolve the SauceBéarnaise service, we must export it. The easiest and most idiomatic way to do that is by annotating the class itself like this:

[Export]
public class SauceBéarnaise : IIngredient { }

Notice the [Export] attribute annotating the SauceBéarnaise class. This is a MEF attribute that declares that the SauceBéarnaise class exports itself. This means that if you put the class in a catalog, you can now resolve the SauceBéarnaise class, but nothing else, because it’s the only export:

var catalog = new TypeCatalog(typeof(SauceBéarnaise));
var container = new CompositionContainer(catalog);
SauceBéarnaise sauce =
    container.GetExportedValue<SauceBéarnaise>();

You already saw a glimpse of the catalog concept in figure 15.2, and we’ll discuss it in greater detail in section 15.1.3. For now, suffice it to say that you package the annotated SauceBéarnaise class into a catalog that you use to define a container. Now that you have the container, you can use it to resolve the SauceBéarnaise service.

 

Note

The GetExportedValue method corresponds directly to Windsor’s, Autofac’s, and Unity’s Resolve methods.

 

Apart from the GetExportedValue method, MEF also supports another style where we first get an export and then later extract the value from the export. In its simplest form, it looks like this:

Lazy<SauceBéarnaise> export =
    container.GetExport<SauceBéarnaise>();
SauceBéarnaise sauce = export.Value;

The GetExport method is a good example of an export being a first-class concept in MEF. It encapsulates the export without necessarily instantiating the part. Creation of the part may be postponed until we query its Value property, but that also depends on the lifetime of the part.

Both the GetExportedValue and GetExport methods have plural counterparts that enable us to resolve sequences of parts. They look like this:

IEnumerable<IIngredient> ingredients =
    container.GetExportedValues<IIngredient>();
IEnumerable<Lazy<IIngredient>> exports =
    container.GetExports<IIngredient>();

So far, the SauceBéarnaise class exports only its own, concrete type. Even though it also implements IIngredient, it doesn’t export that interface unless you explicitly state that it does. Mapping ABSTRACTIONS to concrete types also involves the [Export] attribute.

Mapping Abstractions to concrete types

The [Export] attribute exports the part it annotates. Sometimes the exported part is already an ABSTRACTION, but when we annotate a class, the concrete class is exported by default, even if it implements one or more interfaces.

Loose coupling normally requires us to map ABSTRACTIONS to concrete types. Creating instances based upon such maps is the core service offered by any DI CONTAINER, but we must still define the map. With MEF, we do that by modifying the export by explicitly stating what it exports.

In this example, you let the concrete SauceBéarnaise class export the IIngredient interface:

[Export(typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

Compared to the previous example, you’ve now changed the [Export] attribute to use an overload that enables you to specify that IIngredient is the export. Once again, you can package the SauceBéarnaise class into a catalog and create a container out of the catalog.

IIngredient ingredient = container.GetExportedValue<IIngredient>();

When you resolve IIngredient from the container, the ingredient value now turns out to be a SauceBéarnaise instance, as you would’ve expected. However, if you attempt to resolve SauceBéarnaise as you did in the first example, you’ll now get an exception because there are no parts that export the SauceBéarnaise contract.

You can easily resolve this by applying the Export attribute multiple times:

[Export]
[Export(typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

The [Export] attribute can be applied as many times as needed, so this version exports both the concrete SauceBéarnaise class as well as the IIngredient interface.

So far, we’ve looked at a strongly typed, generic method that can be used to resolve services. Still, there are situations where we need a more weakly typed way to resolve services. This is also possible, although a bit more involved than we might wish.

Resolving weakly typed services

Sometimes we can’t use a generic API because we don’t know the appropriate type at design time. All we have is a Type instance, but we’d still like to get an instance of that type. You saw an example of that in section 7.2, where we discussed ASP.NET MVC’s DefaultControllerFactory class. The relevant method is this one:

protected internal virtual IController GetControllerInstance(
    RequestContext requestContext, Type controllerType);

Because we only have a Type instance, we can’t use generics, but must resort to a weakly typed API. Unfortunately, the only untyped API exposed by CompositionContainer is a little unwieldy. There’s no untyped version of the GetExportedValue or GetExportedValues methods, so we must resort to the non-generic version of GetExports to implement GetControllerInstance:

var export = this.container.GetExports(
    controllerType, null, null).Single();
return (IController)export.Value;

There are several overloads of the non-generic GetExports method, and here we use one that enables us to pass in the controllerType directly. The two other parameters can be used to provide constraints for the query, but we can pass in null when we don’t need to do that. The GetExports method returns a sequence of exports, but we require that there’s only a single export that satisfies the query, so we invoke the Single extension method to get the single instance from the sequence.

Because the GetExports method is weakly typed, we must cast the exported value to IController before returning it. In any case, no matter which specific method we use to resolve parts, MEF composes the parts by matching imports with exports. It can only do this when we have explicitly defined these in advance.

15.1.2. Defining imports and exports

In section 3.2, we discussed several conceptually different ways we can configure a DI CONTAINER. Figure 15.3 reviews the options and illustrates how MEF doesn’t fit into that model at all.

Figure 15.3. MEF doesn’t fit into our standard conceptual model for configuration options, because we can’t configure the container. All the normal options we’re used to reviewing are unavailable and grayed out. Annotating parts with attributes defines the configuration statically together with the type, whereas catalogs provide flexibility.

We can’t configure the container itself in any way—neither with imperative code nor through XML configuration. MEF only gives us one option to define imports and exports, and that is by applying attributes to parts. Attributes are part of the type they annotate, so we must view this mechanism as more explicit and early bound than even CODE AS CONFIGURATION. Catalogs, on the other hand, provide us with a great deal of flexibility because they enable us to pick the types we wish to include into a composition.

 

Warning

The use of attributes tightly couples the configuration to the implementation. In section 9.3.1, we discussed the disadvantages of using attributes for aspects, but the discussion applies in general. Keep in mind that MEF may gain alternatives to attributes in the future.

 

In this section, we’ll look at options for importing and exporting parts, and then in section 15.1.3, we’ll look at catalogs. Although we can’t cover our standard options for configuring a container, we can make some approximations to cover different scenarios. In this section, you’ll see different ways in which we can export and import parts.

Exporting types

In this section, we’ll cover the scenario where we control the classes we wish to export. When we have full control over the source code for the classes that we want to export, we can export a class by applying the [Export] attribute:

[Export]
[Export(typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

The [Export] property can be applied as many times as needed, so that the same class can export different contracts. The SauceBéarnaise class shown here exports both itself as a concrete class and the IIngredient interface.

The [Export] overload that enables you to specify an exported type provides no compile-time checking. You can declare an invalid export without compilation errors:

[Export(typeof(ICourse))]
public class SauceBéarnaise : IIngredient { }

The SauceBéarnaise class doesn’t implement the ICourse interface, yet you can still compile the claim that it does. However, when you attempt to resolve ICourse, an exception will be thrown because MEF can’t cast SauceBéarnaise to ICourse.

 

Warning

It’s possible to declare invalid exports.

 

Obviously, you can let different classes export different contracts without conflict:

[Export(typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

[Export(typeof(ICourse))]
public class Course : ICourse { }

Because each class exports different contracts, there’s no conflict and you can ask the container to resolve both ICourse and IIngredient and receive instances of Course and SauceBéarnaise, respectively.

However, exporting the same ABSTRACTION multiple times changes the picture:

[Export(typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

[Export(typeof(IIngredient))]
public class Steak : IIngredient { }

In this example, you export IIngredient twice. If you attempt to resolve IIngredient, the container will throw an exception because there are multiple exports; by invoking GetExport or GetExportedValue you imply that you request a ubiquitous part. You can still get both SauceBéarnaise and Steak by invoking the plural methods GetExports and GetExportedValues.

 

Note

MEF has no concept of a default component. All exports are equally ranked.

 

This is your first glimpse of an important concept in MEF: cardinality of imports and exports. The number of exports must be compatible with the number of imports. Table 15.2 shows how MEF matches imports and exports based on cardinality.

Table 15.2. Import and export cardinality matches
 

Export.Single

Export.Many

Import.Single Match No match
Import.Many Match Match

In this context, the term many indicates a sequence of parts, typically an array or IEnumerable<T>. If we explicitly import many parts of the same contract, MEF will always find us a match because zero exports is a special case of multiple exports.

On the other hand, when we explicitly import a single instance we get a cardinality mismatch if there are zero or multiple exports because importing a single instance indicates that we must have exactly one, ubiquitous instance.

 

Note

Cardinality is one among several dimensions where imports and exports must match.

 

As you’ll see in section 15.2, LIFETIME MANAGEMENT can also play a role when it comes to matching parts, but cardinality is always active. Later in this section, you’ll see how to define single and multiple imports; but before we cover that we should look at exporting parts when we don’t control the classes involved.

Exporting adapters

Applying the [Export] attribute to a class is the easiest way to export a part, but this might not always be possible. We may wish to export classes that were already compiled, and we might not have access to the source code. In such cases, we can’t apply attributes, yet we would still like to include the class into a composition.

We can still achieve that goal by leveraging MEF’s ability to export properties as well as classes. As an example, consider this Mayonnaise constructor:

public Mayonnaise(EggYolk eggYolk, OliveOil oil)

Imagine that the Mayonnaise class and its constituent EggYolk and OliveOil DEPENDENCIES are outside your control. One option would be to derive from the original class and apply the [Export] attribute to the derived class:

[Export(typeof(OliveOil))]
[Export(typeof(IIngredient))]
public class MefOliveOil : OliveOil { }

Notice that if you wish to export both the original concrete class as well as the IIngredient interface, you must explicitly state that the base class (which is also a concrete class) is being exported. Had you used the [Export] attribute without a type, you would’ve exported the MefOliveOil class instead.

However, if the classes in question are sealed, you can’t export them in this way. Instead, as the following listing shows, you can create an adapter[3] and export the part via a property.

3 I have chosen the term adapter because the purpose of such MEF Adapters corresponds to the purpose, if not all the specifics, of the original Adapter design pattern: Erich Gamma et al., Design Patterns: Elements of Reusable Object-Oriented Software (New York: Addison-Wesley, 1994), 139.

Listing 15.1. Exporting OliveOil via an adapter

The OliveOilAdapter class is a completely new class that wraps the original OliveOil class and exports it through an annotated property . The [Export] attribute can be applied to properties as well as types, but otherwise works in the same way. The type of the OliveOil property is OliveOil, which is also the contract you wish to export, so in this case, you can use the [Export] property without explicitly stating a type.

 

Tip

It’s always possible to export a type by creating an adapter.

 

When the class that you need to compose has DEPENDENCIES of its own, you need to import these through the adapter. As the following listing demonstrates, this becomes a little more involved, but is still quite manageable.

Listing 15.2. Adapting a class with DEPENDENCIES

To export the Mayonnaise class via an adapter, you must address the fact that it has DEPENDENCIES of its own that you need to import. To be able to provide an instance, you must mimic the signature of the Mayonnaise constructor in the adapter’s constructor so that you can import all the necessary parts. After passing appropriate Guard Clauses, you create a new Mayonnaise instance from the constructor parameters and save the result in a private field. This is the CONSTRUCTOR INJECTION pattern at work.

To export Mayonnaise, you can expose the mayo field as a property and annotate it with an [Export] attribute .

With an EggYolkAdapter similar to the OliveOilAdapter from listing 15.1, you can create a catalog from the three adapters and successfully resolve a Mayonnaise instance even though you never modified the original classes.

You may have noticed the [ImportingConstructor] attribute that appeared in listing 15.2. This is part of the other side of the equation. So far, we’ve been looking at how to export parts; now we need to see how to import them.

Importing parts

There’s symmetry about MEF. Most of the statements we can make about exports we can also make about imports. However, when it comes to CONSTRUCTOR INJECTION, we must resort to the [ImportingConstructor] attribute, which has no equivalent for exports. We saw it applied to the MayonnaiseAdapter in listing 15.2, but it must be applied wherever we wish to use CONSTRUCTOR INJECTION.

In the example we assumed that the Mayonnaise class was out of our control. In a sudden reverse of fortune, we have unexpectedly gotten hold of the source code and can now change the types directly. In this case, we don’t have to create adapters, but can apply the [Export] attributes directly to the Mayonnaise, OliveOil, and EggYolk classes.

MEF doesn’t recognize the CONSTRUCTOR INJECTION pattern, so even though Mayonnaise has only a single constructor, we’ll initially get an exception if we attempt to resolve it. We need to explicitly tell MEF which constructor it should use if there’s no default constructor available:

[ImportingConstructor]
public Mayonnaise(EggYolk eggYolk, OliveOil oil)

The [ImportingConstructor] is a signal to MEF that the constructor it annotates should be used to compose the type.

 

Tip

The [ImportingConstructor] isn’t necessary for default constructors. Use it if a class has no default constructor, or if composition should happen through a different constructor than the default.

 

We can also use an [Import] attribute to support PROPERTY INJECTION, but we’ll get back to that in section 15.4.3, which deals explicitly with this pattern. Likewise, there’s an [ImportMany] attribute which is used to import sequences of parts, but we’ll deal with that in section 15.3.2.

Importing and exporting parts relies on applying attributes, and because attributes are compiled into the type, this is about as inflexible as it gets. MEF instead gets its flexibility from catalogs.

15.1.3. Working with catalogs

A catalog encapsulates a collection of parts that the container can use to compose an object graph. In this section, we’ll review various types of catalogs that MEF makes available.

Using catalogs with containers

In section 15.1.1, you already saw an example of a catalog and a container interacting:

var catalog = new
     TypeCatalog(typeof(SauceBéarnaise));
var container = new
     CompositionContainer(catalog);

Here you use a TypeCatalog with a single type, but you can create a CompositionContainer with any ComposablePartCatalog; TypeCatalog is one child class among several. Figure 15.4 sketches the type hierarchy.

Figure 15.4. MEF include four concrete catalogs, but we can also conceivably define custom catalogs. It may be fairly straightforward to implement a catalog that acts as a Decorator for other catalogs (for example, a filtering catalog), whereas a true custom catalog would be more involved.

 

Definition

A catalog is any class that derives from the abstract ComposablePartCatalog class.

 

As the name implies, a ComposablePartCatalog is a catalog of parts that a CompositionContainer uses to match imports with exports. One of the CompositionContainer class’s constructor overloads enables us to supply a ComposablePartCatalog, and this is the constructor we’ve been using so far:

public CompositionContainer(ComposablePartCatalog catalog,
    params ExportProvider[] providers)

In addition to accepting a ComposablePartCatalog instance, this constructor also accepts a params array of ExportProviders, which is another extensibility mechanism outside the scope of this chapter.

Because ComposablePartCatalog is an abstract class and CompositionContainer accepts any derived class, we can in theory create custom catalogs from scratch. This is a major SEAM for MEF and can even be used to define alternatives to the MEF’s default attributed model for defining imports and exports. Although this is possible, it’s also a lot of work, so it isn’t something we’ll cover in this chapter.

 

Tip

The MEF Contrib open source project[4] provides an example of a custom ComposablePartCatalog that completely replaces the attributed configuration model with a more open model that looks more like other DI CONTAINERS.

4http://mefcontrib.codeplex.com/

 

All the catalogs provided with MEF in .NET 4 use [Import] and [Export] attributes to define imports and exports, but they locate parts in different ways. As an example, TypeCatalog locates parts by reading attributes off the types contained in the catalog.

Using type catalogs

The TypeCatalog class lets us define a catalog from a list of types, with the underlying assumption that these types define imports and exports via attributes. There are two overloaded constructors that both enable us to provide an arbitrary number of Type instances:

public TypeCatalog(params Type[] types)
public TypeCatalog(IEnumerable<Type> types)

As an example, to be able to compose Mayonnaise from the adapters you created in listings 15.1 and 15.2, you can create a catalog like this:

var catalog = new TypeCatalog(
    typeof(MayonnaiseAdapter),
    typeof(EggYolkAdapter),
    typeof(OliveOilAdapter));

This is the minimal catalog that enables you to resolve Mayonnaise. If you remove any of the three adapter types, exports would be missing. In addition to letting you resolve Mayonnaise itself, this catalog also enables you to resolve EggYolk and OliveOil, but nothing else.

Obviously you could provide more types to a TypeCatalog to offer more exports, but you must explicitly provide the list of types. This makes sense for small scenarios with limited scope. The advantage is that you can pick only those types from which you wish to compose. If you have types that export competing parts, you can select only the ones you want.

 

Tip

You can create different adapters that export the same part in mutually exclusive ways, and supply only one of them to a TypeCatalog. When writing the sample code for this chapter I used this trick to vary the attributes of MayonnaiseAdapter without having to edit the code.

 

The disadvantage of using a TypeCatalog is that you must explicitly supply all the types. When you add a new type to an assembly, you also need to add it to a TypeCatalog if you want to include it. This violates the DRY[5] Principle. You could get around that issue by writing code that uses Reflection to scan an assembly for all public types, but you don’t have to do that because there’s already a catalog that does exactly that.

5 Don’t Repeat Yourself.

Using assembly catalogs

The AssemblyCatalog class scans an assembly for all imports and exports defined in that assembly. This enables us to keep adding parts to an assembly without having to remember to also add the part to a catalog.

Using AssemblyCatalog is as simple as providing an Assembly instance via the constructor:

var assembly = typeof(Steak).Assembly;
var catalog = new AssemblyCatalog(assembly);

Here you use an indiscriminate representative type (Steak) to define the assembly, but any method that creates the appropriate Assembly instance will do.

There’s also a constructor overload that takes a file name instead of an Assembly instance. This enables more loosely coupled scenarios because we can replace the .dll file without recompiling the rest of the application. This moves us even closer toward MEF’s raison d’être of enabling add-in scenarios. With the AssemblyCatalog, we could write an imperative loop and create a catalog for each file we find in a given directory. However, we don’t need to do that because MEF already provides a dedicated catalog that does that.

Using directory catalogs

The main purpose of MEF is to enable add-in scenarios. A common add-in architecture is to designate a special directory for add-ins; any assembly placed in this directory will be loaded and used by the main application.

MEF supports this scenario through the DirectoryCatalog class. We supply a directory path to the constructor, and it automatically scans that directory for .dll files and loads all parts from the assemblies it finds:

var catalog = new DirectoryCatalog(directory);

An alternative constructor overload also enables us to specify a search pattern using common wildcards.

 

Note

When MEF is used in its key role as an extensibility framework you should expect DirectoryCatalog to be the most commonly used catalog.

 

Although we can place any number of assemblies in a directory and use a DirectoryCatalog to pick them up, we may want to combine catalogs from several different sources. Even if we use a DirectoryCatalog to enable extensibility, we may also want to provide some default or internal implementations of the relevant imports and exports. These shouldn’t reside in the add-in folder because that would enable users to remove vital functionality from the application. Such default implementations may be better provided by a TypeCatalog, but that means that we must combine different catalogs into one.

Using aggregate catalogs

To combine catalogs, we can use an AggregateCatalog, which is a Composite[6] with another name. It aggregates any number of other catalogs while being a catalog in its own right:

6 Gamma, Design Patterns, 163.

var catalog = new AggregateCatalog(catalog1, catalog2);
var container = new CompositionContainer(catalog);

The four catalogs included with MEF already provide a good deal of flexibility, and we can also implement custom catalogs for greater control. One example that is fairly easy to implement and use could be a filtering catalog.

Implementing a filtering catalog

Although it can be a rather intricate undertaking to implement a custom catalog completely from scratch, we can fairly easily implement a Decorator[7] that modifies the behavior of another catalog.

7 Ibid., 175.

The most obvious example is a filtering catalog that filters a decorated catalog. The following listing shows a custom catalog that decorates another catalog and only allows through those parts that export a contract containing the string Sauce; you can use it to get only the sauces from a catalog of all ingredients.

Listing 15.3. Implementing a custom catalog

To implement a custom catalog, you derive from the abstract ComposablePartCatalog class . Because you wish to decorate another catalog you request it via CONSTRUCTOR INJECTION .

The Parts property is the only abstract member of ComposablePartCatalog, so this is the only member you must implement; there are other virtual members that you can implement if you like, but this isn’t necessary for this example. The filter is implemented by a Where expression that filters away all ComposablePartDefinitions that don’t export any contract that contains the word Sauce.

The SauceCatalog is specific, but you can generalize the concept to create a general-purpose FilteringCatalog; the MEF documentation includes an example.[8]

8http://mef.codeplex.com/wikipage?title=Filtering%20Catalogs

 

Custom catalogs

Perhaps listing 15.3 made you wonder: if we only need to implement a single property to create a custom catalog, how can it be so difficult? The problem is that ComposablePartDefinition is an abstract type without any public implementations. Implementing a derived ComposablePartCatalog requires that we also implement a custom ComposablePartDefinition. The pattern is now repeated because ComposablePartDefinition defines another abstract method with a return type that has no public implementation. Although it’s possible to implement a custom catalog, it’s outside the scope of this book.

 

Catalogs are an essential building block of MEF. Whereas attributes are static, catalogs provide a certain flexibility that partially makes up for that. MEF comes with four builtin catalogs that contain parts drawn from explicit types, from a single assembly, or from assemblies found in a folder. When we need to combine parts from multiple catalogs, we can use an AggregateCatalog.

MEF only supports configuration of parts via attributes, but if we need to compose parts without attributes we can always create adapters that import and export those parts. Such adapters can be used to bridge the gap between the static attributed model and the container configuration we’re used to from other DI CONTAINERS. We can subclass the existing catalog types to pre-package a set of parts or adapters that can later be combined with an AggregateCatalog to compose an application.

Until now, we’ve only looked at how to define imports and exports so that MEF can compose object graphs. There are other dimensions of DI that we have yet to look at. One of the most important topics is how to manage OBJECT LIFETIME.

15.2. Managing lifetime

In chapter 8, we discussed LIFETIME MANAGEMENT, including the most common conceptual lifetime styles such as SINGLETON and TRANSIENT. It’s easy to get an overview of the available lifestyles in MEF, because there are only the two shown in table 15.3.

Table 15.3. MEF lifestyles

Name

Comments

Shared You should consider this the default, although it depends on matching imports and exports. This is MEF’s name for the SINGLETON lifestyle.
NonShared MEF’s name for the TRANSIENT lifestyle. Instances are tracked by the container.

 

Note

MEF calls lifestyles creation policies.

 

MEF’s implementation of both SINGLETON and TRANSIENT are equivalent to the general lifestyles described in chapter 8, so I won’t spend much time on them in this chapter.

 

Tip

The default lifestyle in MEF is SINGLETON. This is different from many other containers. As we discussed in chapter 8, SINGLETON is the most efficient, although not always the safest, of all lifestyles, so MEF’s default prioritizes efficiency over safety.

 

There are only two creation policies, and it isn’t possible to implement custom lifetimes, so compared to the other chapters in part 4, this section will be rather short. You’ll see how to declare lifestyles for parts and how to release components. At the end of this section, you should be able to use MEF’s creation policies in your own application.

Consistently with the rest of MEF’s API, creation policy is defined with attributes.

15.2.1. Declaring creation policy

Declaring the creation policy for a part is as easy as adding the [PartCreationPolicy] attribute to a class:

[Export(typeof(IIngredient))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class SauceBéarnaise : IIngredient { }

The [PartCreationPolicy] attribute requires that you specify a CreationPolicy value. Here you specify NonShared to declare SauceBéarnaise as TRANSIENT, but as table 15.4 shows, the CreationPolicy enum has a few other options.

Table 15.4. CreationPolicy values

Value

Description

Any This is the default value. The part can be either a SINGLETON or TRANSIENT, but unless NonShared is explicitly requested, the part will behave as Shared.
Shared The part is a SINGLETON.
NonShared The part is always TRANSIENT.

 

Note

The [PartCreationPolicy] attribute can only be applied to classes. This is different than the [Import] and [Export] attributes that can be applied to classes, members, and parameters.

 

It isn’t surprising that we can specify the values Shared and NonShared, but the Any value may come as a surprise. We’ll look at the Any value shortly, but before we do that we should first complete the short tour of Shared and NonShared.

Exporting with creation policies

As we just discussed, we specify the creation policy with the [PartCreationPolicy] attribute. If we don’t supply this attribute, the default creation policy is Any. However, if we stick with the standard [Import] and [ImportingConstructor] attributes you’ve seen in this chapter so far, this effectively defaults to SINGLETON behavior.

In that context, the following two examples are equivalent:

[Export(typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

[Export(typeof(IIngredient))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class SauceBéarnaise : IIngredient { }

The only difference between the top two and the bottom three lines of code is that in the lower example you explicitly state that the part is a SINGLETON. As long as the importer doesn’t specifically request a particular creation policy, the behavior will be identical.

The difference is that the top example has an implicit creation policy value of Any. Whereas in most cases this will default to SINGLETON behavior, it’s a little more complex than that.

Importing with creation policy requirements

CreationPolicy.Any explicitly states that the creation policy of the part hasn’t been decided, and that matching of imports and export will decide the lifetime.

Among the many options MEF gives us for importing DEPENDENCIES is a feature that enables us to require a part with a certain creation policy. This might look like this:

[ImportingConstructor]
public Mayonnaise(
    [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
    EggYolk eggYolk,
    OliveOil oil)
{ }

This Mayonnaise constructor explicitly states that only fresh egg yolks are accepted. From a culinary perspective, this might not sound so bad, but when it comes to code, this creates a hard constraint on the importing part. This requirement is compiled into the Mayonnaise class via the [Import] attribute that annotates the eggYolk constructor argument. Notice that only the eggYolk parameter is annotated, allowing you to use olive oil from the same bottle to make mayonnaise more than once.

 

Warning

Specifying a creation policy requirement in a compiled attribute on the consumer is a variation of the CONTROL FREAK anti-pattern. MEF allows it, but you should abstain from it, because it constrains your options for composition.

 

The RequiredCreationPolicy property has the potential of changing the context of matching exports with imports. When we don’t use it, we accept anything (Shared and NonShared alike), but when we do use it, incompatible exports will be rejected.

Do you recall table 15.2 that describes how imports and exports are matched on cardinality? Matching on creation policy is another dimension of MEF’s matching algorithm. Table 15.5 shows how creation policies are matched.

Table 15.5. Import and export creation policy matches
 

Export.Any

Export.Shared

Export.NonShared

Import.Any Shared Shared NonShared
Import.Shared Shared Shared No match
Import.NonShared NonShared No match NonShared

Remember that matching on creation policy is only one dimension of matching imports and exports, and all constraints must be satisfied before a match can be made. Keeping imports unspecified with respect to creation policy is by far the preferable option.

In this section, you saw that creation policy is defined with attributes, like imports and exports in general. Specifying creation policies is the first part of LIFETIME MANAGEMENT, but after we resolve object graphs that may contain parts with mixed lifetimes, we should also remember to release them again.

15.2.2. Releasing objects

As discussed in section 8.2.2, it’s important to release objects when we’re done with them so that any disposable instances can be disposed of if their lifetime is up. With MEF, this is fairly easy to accomplish. We can explicitly release exports, or we can dispose of the entire container when we don’t need it any longer.

Releasing exports

Releasing an export is easy, but a quirk of MEF is that, although we can release exports, we can’t release exported values. What’s the difference?

This is an exported value:

var ingredient = container.GetExportedValue<IIngredient>();

As you saw in section 15.1.1, the GetExportedValue method returns an instance of the requested type, so that ingredient is an instance of IIngredient. As an alternative to requesting the exported value, you can request the export:

var x = container.GetExport<IIngredient>();

Instead of returning an IIngredient instance, the GetExport method returns a Lazy<IIngredient>. You can still get the exported value from the Value property on the export:

var ingredient = x.Value;

Whereas x is an instance of Lazy<IIngredient>, ingredient is an instance of IIngredient. When you want to release the resolved components, you must keep the export around because there’s only one Release method available on CompositionContainer:

public void ReleaseExport<T>(Lazy<T> export)

The ReleaseExport method requires the export and not the exported value. This means that you can’t release the exported value directly by supplying the ingredient variable, but must keep the export around to release it:

container.ReleaseExport(x);

Because ingredient was created from x, it’s released when you release the export like that. Disposable DEPENDENCIES are properly disposed of if their lifetime is up. Releasing parts is as easy as invoking the ReleaseExport method, but you must keep around the original export to be able to do it.

It’s important to be able to release exports without disposing of the entire container in scenarios where the same container resolves many instances. This is a typical scenario in web applications and web services where the same container handles multiple requests. On the other hand, in client applications, they should only resolve a single object graph, in which case we can dispose of the container when the application shuts down.

Disposing of the container

Client applications like WPF, Windows Forms, or console applications should follow the pure REGISTER RESOLVE RELEASE pattern, only composing a single object graph for the entire lifetime of the application. This means that we only need to release the object graph at the end of the application’s lifetime.

Although we can still release the export with the ReleaseExport method, an easier alternative that doesn’t require us to keep a reference to the export is to dispose of the container itself. When the application exits, the container is no longer needed, so we can properly release all parts by disposing of the container:

container.Dispose();

Disposing of the container releases all parts, disposing of both SINGLETON and TRANSIENT disposables.

This completes our tour of LIFETIME MANAGEMENT with MEF. Parts can be composed from constituents with mixed lifestyles, and this is even true when we define multiple exports of the same ABSTRACTION. We have yet to look at how to work with multiple parts, so we’ll now turn our attention in that direction.

15.3. Working with multiple components

DI CONTAINERS thrive on distinctness but have a hard time with ambiguity. When using CONSTRUCTOR INJECTION a single constructor is preferred over overloaded constructors because it’s evident which constructor to use when there’s no choice.[9] This is also the case when mapping from ABSTRACTIONS to concrete types. If we attempt to map multiple concrete types to the same ABSTRACTION, we introduce ambiguity.

9 Even in this case, however, MEF still requires the redundant [ImportingConstructor] attribute.

Despite the undesirable qualities of ambiguity, we often need to work with multiple implementations of a single interface. This can be the case in these situations:

  • Different concrete types should be used for different consumers.
  • DEPENDENCIES are sequences.
  • Decorators are in use.

In this section, we’ll look at each of these cases and see how MEF addresses each one in turn. When we’re done, you should be able to annotate and successfully resolve parts even when multiple implementations of the same ABSTRACTION are in play.

 

Note

In this section, we don’t discuss runtime INTERCEPTION because MEF doesn’t support it.

 

Sometimes we need to provide more fine-grained control than what AUTO-WIRING allows. The next section describes how we can do that with MEF.

15.3.1. Selecting among multiple candidates

AUTO-WIRING is convenient and powerful but provides us with little control. As long as all ABSTRACTIONS are distinctly mapped to concrete types we have no problems, but as soon as we introduce more implementations of the same interface, ambiguity rears its ugly head.

First we need a little recap of how MEF deals with multiple exports of the same ABSTRACTION.

Working with multiple implementations of the same export

As you saw in section 15.1.2, you can create multiple parts for the same export:

[Export(typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

[Export(typeof(IIngredient))]
public class Steak : IIngredient { }

This example defines both SauceBéarnaise and Steak as IIngredient exports. However, contrary to most other DI CONTAINERS, MEF has no concept of a default component. There’s either a single export of a part, or there are multiple exports. This is the concept of cardinality that you saw illustrated by table 15.2. When you export both SauceBéarnaise and Steak as IIngredient you have multiple exports and you can only resolve them by importing multiple instances.

With the two exports, trying to resolve a single IIngredient instance throws an exception:

var ingredient = container.GetExportedValue<IIngredient>();

This throws an exception because there are multiple exports of IIngredient and MEF refuses to pick one over the other. This makes a lot of sense when we consider the core scenario of MEF: in extensibility scenarios, we’ll typically get the exports from assemblies in a folder. Exports are add-ins, so at design time we don’t know which exports will be available—if any. In such a context, it doesn’t make much sense to pick a single export at the expense of others. Either we must make the exports unambiguous in some other way, or we must be able to deal with a multitude. We’ll shortly return to the subject of importing multiple components, but first we’ll look at the options we have for making exports more distinct.

One way we can make an export more distinct is by naming it. An overload to the [Export] attribute’s constructor allows us to supply a name for the export:

[Export("sauce", typeof(IIngredient))]
public class SauceBéarnaise : IIngredient { }

[Export("meat", typeof(IIngredient))]
public class Steak : IIngredient { }

Instead of defining two parts that provide the same export (IIngredient), this example defines two different exports: one that exports the combination of IIngredient and the name sauce, and one that exports the combination of IIngredient and the name meat. Now there’s no export of the unnamed IIngredient contract.

 

Note

When exporting only named types, there’s no export for the unnamed type.

 

If you attempt to resolve an unnamed IIngredient, an exception will be thrown:

var ingredient = container.GetExportedValue<IIngredient>();

Although that throws an exception, you can resolve both named IIngredient exports by asking specifically for them:

var meat = container.GetExportedValue<IIngredient>("meat");
var sauce = container.GetExportedValue<IIngredient>("sauce");

Explicitly resolving a named export by using the appropriate overload for GetExportedValue is a good way to demonstrate how parts are resolved, but when we follow the REGISTER RESOLVE RELEASE pattern, it shouldn’t be necessary to request a specifically named component in this way.

 

Tip

If you find yourself invoking the GetExportedValue method with a specific name, consider whether you can change your approach to be less ambiguous.

 

We can use named exports to select among multiple alternatives when defining parts to be matched with a given consumer.

Importing named exports

As useful as AUTO-WIRING is, sometimes we need to override the normal behavior to provide fine-grained control over which DEPENDENCIES go where, but it may also be that we need to address an ambiguous API. As an example, consider this constructor:

public ThreeCourseMeal(ICourse entrée,
    ICourse mainCourse, ICourse dessert)

In this case, you have three identically typed DEPENDENCIES that each represents a different concept. In most cases, you want three distinct exports to fill in the appropriate parameters. The following listing shows how you can annotate the desired classes to provide the necessary exports.

Listing 15.4. Defining named exports
[Export("entrée", typeof(ICourse))]
public class Rillettes : ICourse { }

[Export("mainCourse", typeof(ICourse))]
public class CordonBleu : ICourse { }

[Export("dessert", typeof(ICourse))]
public class MousseAuChocolat : ICourse { }

You annotate the Rillettes with the “entrée” named export, the CordonBleu class with the “mainCourse” named export, and the MousseAuChocolat with the “dessert” named export.

Given these exports, you can annotate the ThreeCourseMeal class’s constructor with matching [Import] attributes like this:

[ImportingConstructor]
public ThreeCourseMeal(
    [Import("entrée", typeof(ICourse))]ICourse entrée,
    [Import("mainCourse", typeof(ICourse))]ICourse mainCourse,
    [Import("dessert", typeof(ICourse))]ICourse dessert)

Notice that you can apply the [Import] attribute to constructor arguments. Normally you don’t need to do this explicitly when you already have the [ImportingConstructor] attribute on the constructor, but in this case you need to annotate each parameter to match a different named export. Because you have the matching exports defined in listing 15.4, you can now successfully resolve ThreeCourseMeal from these parts.

 

Tip

If you can’t (or don’t want to) alter the classes directly, you can create exporting adapters instead.

 

 

Metadata

Matching imports with exports using named contracts is a convenient and easy way to address ambiguity. However, using hard-coded strings as we have done in this chapter isn’t refactoring-safe. We can attempt to remedy this by defining constants which we use instead of hard-coded strings, but this doesn’t ensure that all developers remember to use the constants.

Another alternative is to use MEF’s metadata feature. This enables us to define custom export attributes that encapsulate extra metadata we would like to attach to an export. A full treatment is beyond the scope of this book, but a good overview is provided in Glenn Block’s MSDN article.[10]

10 Block, Managed Extensibility Framework.

 

We can always match named imports with similarly named exports to get rid of ambiguity, but a better solution is to design the underlying API to get rid of that ambiguity. It often leads to a better overall design.

In the next section, you’ll see how you can use the less ambiguous and more flexible approach where you allow any number of courses in a meal. To this end, you must learn how MEF deals with lists and sequences.

15.3.2. Wiring sequences

In section 10.3.2, we discussed how to refactor an explicit ThreeCourseMeal class to the more general-purpose Meal class with this constructor:

public Meal(IEnumerable<ICourse> courses)

In this section, we’ll look at how we can configure MEF to wire up Meal instances with appropriate ICourse DEPENDENCIES. When we’re done, you should have a good idea of the options available to you when you need to configure parts with sequences of DEPENDENCIES.

Auto-wiring sequences

As we discussed in sections 15.1.2 and 15.3.1, cardinality is an explicit concept with MEF. This also means that MEF has an inherent understanding of multiple imports and exports, but that we need to be explicit about it. In section 15.1.2, you saw how you must apply the [ImportingConstructor] attribute to explicitly enable CONSTRUCTOR INJECTION. Although it’s necessary to apply the [ImportingConstructor] attribute to the Meal constructor, it isn’t enough. This instructs MEF that the Meal constructor should be used for composition, but the implied import here is IEnumerable<ICourse>.

You can export ICourse parts in a manner similar to listing 15.4. However, now that you don’t want to explicitly distinguish between them, none of them should be named:

[Export(typeof(ICourse))]
public class Rillettes : ICourse { }

[Export(typeof(ICourse))]
public class CordonBleu : ICourse { }

[Export(typeof(ICourse))]
public class MousseAuChocolat : ICourse { }

Notice that the only difference from listing 15.4 is that none of the exports are named. You now have multiple exports of ICourse, but that doesn’t in itself bridge the gap from multiple ICourse exports to a single import of IEnumerable<ICourse>. The last step is to apply the [ImportMany] attribute:

[ImportingConstructor]
public Meal([ImportMany]IEnumerable<ICourse> courses)

The [ImportMany] attribute is used to explicitly map multiple exports to a single import of a sequence. The exports can originate from different assemblies but will be composed into a single sequence. When you resolve IMeal, you get a Meal instance with the three ICourse exports: Rillettes, CordonBleu, and MousseAuChocolat.

Using the [ImportMany] attribute, a part can import a sequence of all the exports that match. Only when we need to explicitly pick only some instances from a larger set do we need to do more. Let’s see how to do that.

Picking only some exports from a larger set

When we deal with a multitude of exports, the strategy implied by [ImportMany] is often the correct policy. This provides the importer with all the exports of the desired contract, but as figure 15.5 shows, there may be cases where we want to pick only some exports from the larger set of all exports.

Figure 15.5. In the situation on the left, we wish to explicitly select only certain DEPENDENCIES from the larger list of all exports. This is different from the situation on the right, where we indiscriminately want them all.

When we previously let MEF AUTO-WIRE all exports it corresponded to the situation depicted on the right side of figure 15.5. If we want to compose a part as shown on the left side, we must explicitly define which exports should be used.

The only way we can do that is to once more resort to named exports. However, compared to listing 15.4, the solution is a bit different because now we want to use a named export to mark all those exports we wish to import into the Meal class. As the following listing demonstrates, this doesn’t preclude the parts from exporting other contracts, as well as a set-based contract.

Listing 15.5. Targeting exports for a set

The three classes Rillettes, CordonBleu, and MousseAuChocolat all export a contract with the name meal . This named contract can be used to import only those parts that export this particular contract. However, for other consumers that may want all ICourse exports irrespective of the name, you can also export these three classes as the unnamed ICourse contract ; you can add as many [Export] attributes to a part as you’d like.

The LobsterBisque class only exports the unnamed ICourse contract, but not the named meal contract . This means that those consumers that wish to import all ICourse exports can do that using the default [ImportMany] attribute as you saw before. However, you can also state that a part only imports those parts that explicitly export the named meal contract:

Instead of using the default constructor of the [ImportMany] attribute, you can use a constructor overload that enables you to import only a named contract . The attribute annotates the courses parameter, which means that only those parts that export the named meal contract will be composed into the courses sequence. Given the exports from listing 15.5, you’d end up with a Meal with Rillettes, CordonBleu, and MousseAuChocolat, but without the LobsterBisque.

Named exports can be used as markers so that the marked exports can be selectively composed into consumers. Because you can apply as many [Export] attributes as you’d like, you can mark an export for more than a single purpose.

In both of the cases shown in figure 15.5, the [ImportMany] attribute is the key to importing a multitude of exports into a single consumer. Importing sequences is an important way of dealing with ambiguity, and with its concept of cardinality and explicit attributes, MEF makes this clear.

Consumers that rely on sequences of DEPENDENCIES may be the most intuitive example of having multiple exports of the same ABSTRACTION, but before we leave this subject completely, we need to look at one last, and perhaps a bit surprising, case where multiple exports come into play.

15.3.3. Wiring Decorators

In section 9.1.2, we discussed how the Decorator design pattern is useful when implementing CROSS-CUTTING CONCERNS. By definition, Decorators introduce multiple types of the same ABSTRACTION. At the very least, we have two implementations of an ABSTRACTION: the Decorator itself and the decorated type. If we stack the Decorators, we may have even more.

This is another example of having multiple exports of the same contract. Unlike the previous sections, these exports aren’t conceptually equal, but rather are DEPENDENCIES of each other. In this section, you’ll see how to configure parts to deal with this pattern. There are several ways we can compose Decorators with MEF, but because they’re all similar, we’ll look at only one.

Decorating with concrete contracts

Consider our trusty Breading class, which is a Decorator of IIngredient. It uses CONSTRUCTOR INJECTION to receive the instance it should decorate:

public Breading(IIngredient ingredient)

To make a Cotoletta, you’d like to decorate a VealCutlet (another IIngredient) with the Breading class. One way you can accomplish this is to link the VealCutlet together with the Breading class using the concrete VealCutlet class as a contract:

[Export(typeof(VealCutlet))]
public class VealCutlet : IIngredient { }

Notice that the VealCutlet part only exports the concrete type, but not IIngredient—even though it implements the interface. The Breading constructor can now explicitly state that it imports the concrete VealCutlet contract:

[ImportingConstructor]
public Breading(
    [Import(typeof(VealCutlet))]
    IIngredient ingredient)

MEF matches exports and imports, so as long as there’s an unambiguous match, the composition succeeds. VealCutlet implements IIngredient, so even though the matching algorithm uses the concrete type as a contract, the parts are still compatible. However, note that the compiler doesn’t guarantee this.

 

Note

This approach is conceptually similar to the approach outlined in section 14.3.3 where we used Unity to compose Breading and VealCutlet via the concrete VealCutlet class.

 

 

Note

Because the attribute is compiled into the class, it would’ve been even simpler to change the Breading constructor to take a VealCutlet as a parameter instead of IIngredient. In my opinion, this is an excellent demonstration of the shortcomings of using attributes to guide composition.

 

Although the VealCutlet class implements IIngredient, it doesn’t export it. This is an essential part of this approach. If VealCutlet had also exported IIngredient it would’ve introduced ambiguity because Breading already exports the interface. This would result in a cardinality mismatch because there would now be two exports of IIngredient, and you wouldn’t be able to resolve Breading by importing IIngredient.

 

Warning

Wiring Decorators isn’t possible if the decorated export must also export its ABSTRACTION.

 

The Breading and VealCutlet parts are composed together because they have matching contracts. The exact form of the contract is less important. In this example, you used the concrete type of the decorated class, but you also could’ve used a named contract, or for that matter, any distinct string. The important part is that the match between the two parts is unambiguous.

MEF enables us to work with multiple exports in several different ways. We can configure exports as alternatives to each other, as peers resolved as sequences, or as hierarchical Decorators. In every case we must explicitly specify how MEF should match imports and exports.

This is also the case when we need to deal with APIs that deviate from CONSTRUCTOR INJECTION. So far you’ve seen how to compose parts, including how to specify creation policies and how to deal with multiple exports, but until now we have only used CONSTRUCTOR INJECTION. Sometimes we must deal with other patterns and APIs, so in the next section, we’ll review how we can deal with classes that must be instantiated in special ways.

15.4. Composing difficult APIs

Until now, we’ve considered how we can compose parts that use CONSTRUCTOR INJECTION. As a general observation, one of the many benefits of CONSTRUCTOR INJECTION is that DI CONTAINERS can easily understand how to compose and create all classes in a dependency graph. MEF, on the other hand, requires explicit use of the [ImportingConstructor] attribute, so this is less true for MEF.

In this section, you’ll see how to deal with primitive constructor arguments, static factories, and PROPERTY INJECTION. These all require special attention. Let’s start by looking at classes that take primitive types, such as strings or integers as constructor arguments.

15.4.1. Compositing primitive parts

As long as we inject ABSTRACTIONS into consumers all is well. However, it becomes more difficult when a constructor depends on a primitive type, such as a string, a number, or an enum. This is particularly the case for data access implementations that take a connection string as a constructor parameter, but is a more general issue that applies to all strings and numbers.

Conceptually, it doesn’t always make much sense to register a string or number as a contract. What does it mean to import a string or a number if the type is the only thing we have to go by? Do we really want just any string? Most of the time, we want a specific string, such as a connection string. The same sort of consideration can be applied to any primitive value, including strings, numbers, and enums.

Consider, as an example, this constructor:

public ChiliConCarne(Spiciness spiciness)

In this example, Spiciness is an enum:

public enum Spiciness
{
    Mild = 0,
    Medium,
    Hot
}

 

Warning

As a rule of thumb, enums are code smells and should be refactored to polymorphic classes.[11] But they serve us well for this example.

11 Martin Fowler et al., Refactoring: Improving the Design of Existing Code. (New York: Addison-Wesley, 1999), 82.

 

To properly annotate ChiliConCarne, you can add the [ImportingConstructor] attribute to the constructor. To export Spiciness it makes most sense to do it via an adapter:

public class SpicinessAdapter
{
    [Export]
    public Spiciness Spiciness
    {
        get { return Spiciness.Hot; }
    }
}

This adapter exports the value Spiciness.Hot so that if you compose ChiliConCarne from a catalog containing those parts, we’ll get a hot Chili con Carne.

 

Tip

Instead of exporting and importing the Spiciness type itself, you can instead choose to use a custom string as a shared contract. That would require you to add an additional [Import] attribute to the spiciness constructor argument to specify the contract.

 

With adapters and contracts, we can properly match primitive types with imports. This still works well when all types and constructors are public, but how do we deal with types without public constructors?

15.4.2. Composing parts with non-public constructors

Some classes can’t be instantiated through a public constructor. Instead, we must use some sort of factory to create instances of the type. This is always troublesome for DI CONTAINERS because by default they look after public constructors.

Consider this example constructor for the public JunkFood class:

internal JunkFood(string name)

Even though the JunkFood class is public, the constructor is internal. Obviously, instances of JunkFood should be created through the static JunkFoodFactory class:

public static class JunkFoodFactory
{
    public static IMeal Create(string name)
    {
        return new JunkFood(name);
    }
}

Assuming that you can’t change this API, then how do you deal with this situation so that you can properly wire and compose JunkFood? The answer is the same as in any other case where you can’t change the original exported type: you use an adapter like the one in the following listing.

Listing 15.6. Exporting a type with an internal constructor
public class JunkFoodAdapter
{
    private readonly IMeal junk;

    public JunkFoodAdapter()
    {
        this.junk = JunkFoodFactory.Create("chicken meal");
    }
    [Export]
    public IMeal JunkFood
    {
        get { return this.junk; }
    }
}

The JunkFoodAdapter encapsulates the knowledge that a JunkFood instance is created with the JunkFoodFactory.Create method. It creates the instance in the constructor and exports it via a property. Because the type of the property is IMeal, this is also the exported contract.

With the JunkFoodAdapter class available in a catalog, you can successfully resolve IMeal and get back a “chicken meal” JunkFood instance.

The last common deviation from CONSTRUCTOR INJECTION we’ll examine here is PROPERTY INJECTION.

15.4.3. Wiring with Property Injection

PROPERTY INJECTION is a less well-defined form of DI because we aren’t forced by the compiler to assign a value to a writable property. Ironically, MEF is designed with PROPERTY INJECTION in mind much more than CONSTRUCTOR INJECTION. This explains why we need to explicitly apply attributes to everything we wish to compose: from MEF’s perspective, the default composition pattern is PROPERTY INJECTION (which is ambiguous) and CONSTRUCTOR INJECTION is a less idiomatic alternative.

Although I consider this perspective both backwards and wrong, it does make PROPERTY INJECTION easy to apply with MEF. All we have to do is apply the [Import] attribute to a property.

Consider this CaesarSalad class:

public class CaesarSalad : ICourse
{
    public IIngredient Extra { get; set; }
}

It is a common misconception that a Caesar Salad includes chicken; this isn’t the case. At its core, a Caesar Salad is a salad, but it tastes great with chicken, so many restaurants offer chicken as an extra ingredient. The CaesarSalad class models this by exposing a writable property named Extra.

To enable PROPERTY INJECTION for CaesarSalad, all you have to do is to apply the [Import] attribute:

[Import(AllowDefault = true)]
public IIngredient Extra { get; set; }

In this book, I consistently consider PROPERTY INJECTION a pattern that applies when it’s optional to supply the DEPENDENCY from the outside. This makes sense because the compiler doesn’t force you to assign a value to the property (as opposed to a constructor argument). But this isn’t the view taken by MEF. By default, an import must be satisfied unless you explicitly state that it’s optional, using the AllowDefault property. To stay true to the PROPERTY INJECTION pattern as described here, you set the AllowDefault property to true. This means that MEF will not throw an exception when it can’t satisfy the IIngredient import.

You should be aware that with AllowDefault set to true, MEF will explicitly assign the default value (in this case null) to the property instead of ignoring it if it can’t satisfy the import. To utilize this feature, you must be ready to deal with null values, but this can wreak havoc with the class’s invariants; you should go to great lengths to avoid assigning null to private fields.

One way to deal with null values is to silently swallow such a value, like this:

You can explicitly check for null and return if the caller attempts to inject null . This violates the Principle of Least Astonishment, because callers may be surprised to find that assigning a value has no effect, even though no exception is thrown. Once again, you’re left with the experience that PROPERTY INJECTION is a more problematic pattern which is best avoided unless absolutely warranted.

On the surface, PROPERTY INJECTION is the idiomatic use case for MEF, but as is so often the case, the devil is in the details. Even with MEF, I’d prefer to use CONSTRUCTOR INJECTION as my default approach.

In this section, you’ve seen how we can use MEF to deal with more difficult creational APIs. PROPERTY INJECTION is easy to apply, and the rest we can address with exporting adapters. This is always the universal solution if all else fails and we can’t change the parts.

15.5. Summary

Among all the DI CONTAINERS covered in part 4, MEF is special in more than one way. First, it’s the only composition technology officially delivered and supported by Microsoft. Second, it isn’t a real DI CONTAINER, but rather an extensibility framework (as the name Managed Extensibility Framework implies), so examining it as if it were a DI CONTAINER isn’t entirely fair.

There are so many similarities between MEF and real DI CONTAINERS that this chapter isn’t only warranted, but necessary. You need to understand in what ways MEF isn’t a DI CONTAINER to make an informed decision on when to use it and when not to use it.

This chapter demonstrated that we can squeeze a lot of regular DI CONTAINER functionality out of MEF, but often in rather awkward ways. The most problematic part of MEF is the dependency on attributes because it tightly couples issues such as lifetime and set-based selection of imports to the type. In extensibility scenarios this isn’t an issue, but when we work with composition of a full application, this constraint becomes unwieldy.

When we can’t, or don’t wish to, annotate our types with MEF attributes we can create adapters that import and export the appropriate parts on behalf of the real implementation. In many ways, we can think of such MEF Adapters as MEF’s configuration API, but compared to most other DI CONTAINERS’ strongly typed fluent interfaces, this is still clunky. However, a MEF Adapter is a universally applicable trick we can use to address particular challenges with MEF. Not only can we use them to compose unattributed types with MEF, but we can also use them to export parts from factory methods, and so on.

Does it make sense to use MEF as a DI CONTAINER in an application? As always, the answer depends on circumstances. One of the strong arguments for using MEF is that it ships as part of .NET 4 and Silverlight 4, so if the application targets these platforms, MEF is already available. This isn’t only a question of convenience, but can also be a huge benefit in organizations where policies dictate that only official Microsoft technologies can be used.

Because MEF is an official Microsoft product we also get a different level of support for MEF than we do for other DI CONTAINERS. We get the same support that we get for the rest of .NET and Silverlight, and we can have confidence that MEF is going to be around for a long time.

Still, these benefits may not outweigh the disadvantages of using MEF in a role it was not designed for. MEF was designed for extensibility scenarios, so it makes a lot of sense to use it in applications where extensibility is a major feature. In such applications it may make sense to expand the responsibility of MEF to include composition of the overall application, because it’s already in use.

For applications where extensibility isn’t a feature it may make more sense to select a dedicated DI CONTAINER for object composition.

No matter which DI CONTAINER you select, or even if you prefer POOR MAN’S DI, I hope that this book has conveyed one important point: DI doesn’t rely on a particular technology, such as a particular DI CONTAINER. An application can, and should, be designed using the DI-friendly patterns and practices presented in this book. When we succeed in doing that, selection of a DI CONTAINER becomes of less importance; a DI CONTAINER is a tool that composes our application, but ideally, we should be able to replace one container with another without rewriting any other part of our application than the COMPOSITION ROOT.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.158.32