Top Banner
Hosting MEF in an application Hosting MEF in an application involves creating an instance of the CompositionContainer, adding Composable Parts to it, including the application host itself and then composing. Below are the steps involved with hosting. 1. Create a host class. In the sample below we are using a console application, so the host is the Program class. 2. Add a reference to the System.ComponentModel.Composition assembly. 3. Add the following using statement: using System.ComponentModel.Composition; 4. Add a Compose() method which creates an instance of the container and composes the host. 5. Add a Run() method which calls Compose(); 6. In the Main() method instantiate the host class. Note: For an ASP.NET or WPF application the host class is instantiated by the runtime making this step unnecessary. The code snippet below indicates how the code should look using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System; public class Program { public static void Main(string[] args) { Program p = new Program(); p.Run(); } public void Run() { Compose(); } private void Compose() { var container = new CompositionContainer(); container.ComposeParts(this); } }
36
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: MEF

Hosting MEF in an applicationHosting MEF in an application involves creating an instance of the CompositionContainer, adding Composable Parts to it, including the application host itself and then composing.

Below are the steps involved with hosting.

1. Create a host class. In the sample below we are using a console application, so the host is the Program class. 2. Add a reference to the System.ComponentModel.Composition assembly. 3. Add the following using statement: using System.ComponentModel.Composition;4. Add a Compose() method which creates an instance of the container and composes the host.5. Add a Run() method which calls Compose();6. In the Main() method instantiate the host class.

Note: For an ASP.NET or WPF application the host class is instantiated by the runtime making this step unnecessary.

The code snippet below indicates how the code should look

using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System;

public class Program { public static void Main(string[] args) { Program p = new Program(); p.Run(); }

public void Run() { Compose(); }

private void Compose() { var container = new CompositionContainer(); container.ComposeParts(this); } }

7. Define one or more exports which the host will import. In the code below we've defined an IMessageSender interface. We've also defined an EmailSender Composable Part that exports an IMessageSender which it declares through the [System.ComponentModel.Composition.Export] attribute.

public interface IMessageSender

Page 2: MEF

{ void Send(string message); }

[Export(typeof(IMessageSender))] public class EmailSender : IMessageSender { public void Send(string message) { Console.WriteLine(message); } }

8. Add properties to the host class for each import which are decorated with the [System.ComponentModel.Composition.Import] attribute. For example below is an import for IMessageSender that is added to the Program class.

[Import] public IMessageSender MessageSender { get; set; }

9. Add parts to the container. In MEF, there are several ways in which to do this. One way is by directly adding existing Composable Part instances, while a second, more common approach is through the use of catalogs, which we will mention after the section below.

Adding parts directly to the container

In the Compose() method manually add each Composable Part by using the ComposeParts() extension method. In the example below, an instance of the EmailSender added to the container along with the current instance of the Program class which imports it.

private void Compose() { var container = new CompositionContainer(); container.ComposeParts(this, new EmailSender()); }

Adding to the container using an AssemblyCatalog

By using the catalog, the container handles creating parts automatically rather than them having to be added explicitly. To do this, create a catalog in the Compose() method. Next create a resolver off of the catalog and pass it to the container's constructor.

In the example below an AssemblyCatalog is created with the executing assembly passed into the constructor. We're not adding an instance of EmailSender as it it will be discovered in the catalog that was passed for the current assembly.

Page 3: MEF

private void Compose() { var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container.ComposeParts(this); }

After following each of the above steps, the code should look as shown below.

using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System;

public class Program { [Import] public IMessageSender MessageSender { get; set; }

public static void Main(string[] args) { Program p = new Program(); p.Run(); }

public void Run() { Compose(); MessageSender.Send("Message Sent"); }

private void Compose() { AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var container = new CompositionContainer(catalog); container.ComposeParts(this); } }

public interface IMessageSender { void Send(string message); }

[Export(typeof(IMessageSender))] public class EmailSender : IMessageSender { public void Send(string message) { Console.WriteLine(message);

Page 4: MEF

} }

When the above code is compiled and executed, the application will be composed with its IMessageSender import. The Send() method will then be called which will output "Message Sent" on the console.

Defining Composable Parts and ContractsComposable PartsA Composable Part is a composable unit within MEF. Composable Parts export services that other Composable Parts need, and import services from other Composable Parts. In the MEF programming model, Composable Parts are attributed with the System.ComponentModel.Composition.Import and [System.ComponentModel.Composition.Export] attribute in order to declare their exports and imports. A Composable Part should contain at least one export. Composable Parts are either added to the container explicity or created through the use of catalogs. The default catalogs that MEF ship with identify Composable Parts through the presence of an export attribute.

ContractsComposable Parts do not directly depend on one another, instead they depend on a contract, which is a string identifier. Every export has a contract, and every import declares the contract it needs. The container uses the contract information to match up imports to exports. If no contract is specified, MEF will implicitly use the fully qualified name of the type as the contract. If a type is passed, it will also use the fully qualified name.

Note: By default a type should be passed for a contract, and not a string. Although contracts can be an arbitrary string this can lead to ambiguity. For example "Sender" might overlap with another implementation of "Sender" in a different library. For this reason if you do need to specify a string constract, it is recommend that contract names should be qualified with a namespace that includes the Company Name for example "Contoso.Exports.Sender".

In the code snippet below, all export contracts are equivalent.

namespace MEFSample { [Export] public class Exporter {...}

[Export(typeof(Exporter))] public class Exporter1 {...}

[Export("MEFSample.Exporter")] public class Exporter2 {...}

Page 5: MEF

}

Interface / Abstract contracts

A common pattern is for a Composable Part to export an interface or an abstract type contract rather than a concrete type. This allows the importer to be completely decoupled from the specific implementation of the export it is importing resulting in a separation of concerns. For example below you can see there are two sender implementations that both export IMessageSender. The Notifier class imports a collection of IMessageSender which it invokes in its Send() method. New message senders can now easily be added to the system.

[Export(typeof(IMessageSender))] public class EmailSender : IMessageSender { ... }

[Export(typeof(IMessageSender))] public class TCPSender : IMessageSender { ... }

public class Notifier { [ImportMany] public IEnumerable<IMessageSender> Senders {get; set;} public void Notify(string message) { foreach(IMessageSender sender in Senders) sender.Send(message); } }

Contract AssembliesA common pattern when building extensible applications with MEF is to deploy a contract assembly. A contract assembly is simply an assembly which contains contract types that extenders can use for extending your app. Commonly these will be interfaces, but they may be abstract classes. Additonally contract assemblies will likely contain metadata view interfaces that importers will use, as well as any custom MEF export attributes.Note: You must specify the specific interface type (IMessageSender) being exported otherwise the type (EmailSender) itself will be exported.

Declaring ExportsComposable Parts declare exports through the [System.ComponentModel.Composition.ExportAttribute] attribute. In MEF there are several different ways to declare exports including at the Part level, and through Properties and Methods.

Composable Part exportsA Composable Part level export is used when a Composable Part needs to export itself. In order for a Composable Part to export itself, simply decorate the Composable Part with a [System.ComponentModel.Composition.ExportAttribute] attribute as is shown in the code snippet below.

Page 6: MEF

[Export]public class SomeComposablePart { ...}

Property exportsParts can also export properties. Property exports are advantageous for several reasons.

They allow exporting sealed types such as the core CLR types, or other third party types. They allow decoupling the export from how the export is created. For example exporting the

existing HttpContext which the runtime creates for you. They allow having a family of related exports in the same Composable Part, such as a

DefaultSendersRegistry Composable Part that exports a default set of senders as properties.For example you might have a Configuration class that exports an integer with a "Timeout" contract as in the example below.

public class Configuration { [Export("Timeout")] public int Timeout { get { return int.Parse(ConfigurationManager.AppSettings["Timeout"]); } } } [Export] public class UsesTimeout { [Import("Timeout")] public int Timeout { get; set; } }

Method exportsA method export is where a Part exports one its methods. Methods are exported as delegates which are specified in the Export contract. Method exports have several benefits including the following.

They allow finer grained control as to what is exported. For example, a rules engine might import a set of pluggable method exports.

They shield the caller from any knowledge of the type. They can be generated through light code gen, which you cannot do with the other exports.

Note: Method exports may have no more than 4 arguments due to a framework limitation.

In the example below, the MessageSender class exports its Send method as an Action<string> delegate. The Processor imports the same delegate.

public class MessageSender { [Export(typeof(Action<string>))]

Page 7: MEF

public void Send(string message) { Console.WriteLine(message); } }

[Export] public class Processor { [Import(typeof(Action<string>))] public Action<string> MessageSender { get; set; }

public void Send() { MessageSender("Processed"); } }

You can also export and import methods by using a simple string contract. For example below the "Sender" contract is used.

public class MessageSender { [Export("MessageSender")] public void Send(string message) { Console.WriteLine(message); } }

[Export] public class Processor { [Import("MessageSender")] public Action<string> MessageSender { get; set; }

public void Send() { MessageSender("Processed"); } }

Note: When doing method exports, you are required to either provide a type or a string contract name, and cannot leave it blank.

Inherited ExportsMEF supports the ability for a base class / interface to define exports which are automatically inherited by implementers. This is ideal for integration with legacy frameworks which want to take advantage of MEF for discovery but do not want to require modifying existing customer code. In order to provide this capability use the System.ComponentModel.Composition.InheritedExportAttribute. For example below

Page 8: MEF

ILogger has an InheritedExport. Logger implements ILogger thus it automatically exports ILogger.

[InheritedExport]public interface ILogger { void Log(string message);}

public class Logger : ILogger { public void Log(string message);}

Discovering non-public Composable PartsMEF supports discovery of public and non-public Parts. You don't need to do anything to enable this behavior. Please note that in medium/partial trust environments (including Silverlight) non-public composition will not be supported.

Declaring ImportsComposable Parts declare imports [System.ComponentModel.Composition.ImportAttribute] attribute. Similar to exports, there are several different methods namely through Fields, Properties and Constructor Parameters.

Property ImportsTo import a value to a property, decorate the property with the [System.ComponentModel.Composition.ImportAttribute]. For example the snippet below imports an IMessageSender

class Program { [Import] public IMessageSender MessageSender { get; set; } }

Constructor ParametersYou can also specify imports through constructor parameters. This means that instead of adding properties for each import, you add parameters to a constructor for each import. To use this, follow the following steps.

1. Add a [System.ComponentModel.Composition.ImportingConstructorAttribute] attribute to the constructor that should be used by MEF. 2. Add parameters to the constructor for each import.

For example the code below imports a message sender in the constructor of the Program class.

Page 9: MEF

class Program { [ImportingConstructor] public Program(IMessageSender messageSender) { ... } }

Parameter imports

There are several different different ways to define imports on the constructor.

1. Implied import - By default the container will use the type of the parameter to identify the contract. For example in the code below, the IMessageSender contract will be used.

class Program { [ImportingConstructor] public Program(IMessageSender messageSender) { } }

2. Explicit import - If you want to specify the contract to be imported add an [System.ComponentModel.Composition.ImportAttribute] attribute to the parameter.

Field ImportsMEF also supports importing values directly to fields.

class Program { [Import] private IMessageSender _messageSender; }

Note: note that importing or exporting private members (fields, properties and methods) while supported in full trust is likely to be problematic on medium/partial trust.

Page 10: MEF

Optional importsMEF allows you to specify that an import is optional. When you enable this, the container will provide an export if one is available otherwise it will set the import to Default(T). To make an import optional, set AllowDefault=true on the import as below.

[Export]public class OrderController { private ILogger _logger;

[ImportingConstructor] public OrderController([Import(AllowDefault=true)] ILogger logger) { if(logger == null) logger = new DefaultLogger(); _logger = logger; }}

OrderController optionally imports a logger. If the logger is not present, it will set it's private _logger to a new DefaultLogger instance otherwise it will use the imported logger.

Importing collectionsIn addition to single imports, you can import collections with the ImportMany attribute. This means that all instances of the specific contract will be imported from the container.

MEF parts can also support recomposition. This means that as new exports become available in the container, collections are automatically updated with the new set. For example below the Notifier class imports a collection of IMessageSender. This means if there are 3 exports of IMessageSender available in the container, they will be pushed in to the Senders property during compositon.

public class Notifier { [ImportMany(AllowRecomposition=true)] public IEnumerable<IMessageSender> Senders {get; set;}

public void Notify(string message) { foreach(IMessageSender sender in Senders) { sender.Send(message); } } }

IPartImportsSatisfiedNotificationIn some situations it may be important for your class to be notified when MEF is done with the import process for your class instance. If that's the case implement the [System.ComponentModel.Composition.IPartImportsSatisfiedNotification] interface.

Page 11: MEF

This interface has only a single method: OnImportsSatisfied, which is called when all imports that could be satisfied have been satisfied.

public class Program : IPartImportsSatisfiedNotification { [ImportMany] public IEnumerable<IMessageSender> Senders {get; set;}

public void OnImportsSatisfied() { // when this is called, all imports that could be satisfied have been satisfied. } }

Lazy ExportsDuring composition of a part, an import will trigger the instantiation of a part (or parts) that expose the necessary exports required for the original requested part. For some applications delaying this instantiation – and preventing the recursive composition down the graph – may be an important factor to consider as creation a long and complex graph of objects can be expensive and unnecessary.

This is the motivation for MEF to support what we call lazy exports. In order to use it all you need to do is to import an [System.Lazy<T>] instead of [T] directly. For example, consider the code snippet bellow:

public class HttpServerHealthMonitor{ [Import] public IMessageSender Sender { get; set; }Public Class HttpServerHealthMonitor

The code above imports states that it depends on a contract (IMessageSender) implementation. When MEF supply this dependency it will need to also create the IMessageSender selected and recursively the dependencies that the implementation might have. In order to turn this import to be lazy, you just need to replace it by and Lazy<IMessageSender>:

[Export]public class HttpServerHealthMonitor{ [Import] public Lazy<IMessageSender> Sender { get; set; })

Page 12: MEF

In this case you are opt-in for delaying this instantiation until you actually need the implementation instance. In order to request the instance, use the property [Lazy<T>.Value]. Exports and Metadata

Declaring Exports explained the basics of parts exporting services and values. In some cases it’s necessary to associate information with exports for a variety of reasons. Commonly it’s used to explain about the capabilities of an specific implementation of a common contract. This is useful to allow imports to either constraint the export that can satisfy it, or to import all available implementations at the time and check their capabilities in runtime before using the export.

Attaching Metadata to an ExportConsider the IMessageSender service introduced earlier. Suppose we have a few implementations, and they have differences that may be relevant to the consumer of the implementations. For our example the transport of the message and whether is secure are important information for a consumer (importer).

Using ExportMetadataAttributeAll we have to do to attach this information is to use the [System.ComponentModel.Composition.ExportMetadataAttribute]:

public interface IMessageSender{ void Send(string message);}

[Export(typeof(IMessageSender))][ExportMetadata("transport", "smtp")]public class EmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}

[Export(typeof(IMessageSender))][ExportMetadata("transport", "smtp")][ExportMetadata("secure", null)]public class SecureEmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}

[Export(typeof(IMessageSender))][ExportMetadata("transport", "phone_network")]

Page 13: MEF

public class SMSSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}

Using a Custom Export AttributeIn order to do it more strongly typed than using the ExportMetadataAttribute, you need to create your own attribute and decorate it with [System.ComponentModel.Composition.MetadataAttribute]. In this example we also derive from ExportAttribute, thus creating a custom Export attribute that also specifies metadata.

[MetadataAttribute][AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]public class MessageSenderAttribute : ExportAttribute{ public MessageSenderAttribute() : base(typeof(IMessageSender)) { } public MessageTransport Transport { get; set; } public bool IsSecure { get; set; }}

public enum MessageTransport{ Undefined, Smtp, PhoneNetwork, Other}

Above, the MetadataAttribute is applied to our custom export attribute. The next step is to apply the attribute to our IMessageSender implementations:

[MessageSender(Transport=MessageTransport.Smtp)]public class EmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}

[MessageSender(Transport=MessageTransport.Smtp, IsSecure=true)]public class SecureEmailSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }

Page 14: MEF

}

[MessageSender(Transport=MessageTransport.PhoneNetwork)]public class SMSSender : IMessageSender{ public void Send(string message) { Console.WriteLine(message); }}

That’s all that is required on the export side. Under the hood, MEF is still populating a dictionary, but this fact becomes invisible to you.

Note: You can also create metadata attributes that are not themselves exports, by creating an attribute which is decorated with MetadataAttributeAttribute. In these cases the metadata will be added to Exports on the same member where the custom metadata attribute was applied.

Importing MetadataImporters can access the metadata attached to the exports.

Using Strongly-typed MetadataTo access metadata in a strongly-typed fashion created a metadata view by definining an interface with matching read only properties (names and types). For our sample it would be an interface like the following:

public interface IMessageSenderCapabilities{ MessageTransport Transport { get; } bool IsSecure { get; }}

Then you can start importing using the type System.Lazy<T, TMetadata> where T is the contract type and TMetadata is the interface you’ve created.

[Export]public class HttpServerHealthMonitor{ [ImportMany] public Lazy<IMessageSender, IMessageSenderCapabilities>[] Senders { get; set; }

public void SendNotification() { foreach(var sender in Senders) { if (sender.Metadata.Transport == MessageTransport.Smtp && sender.Metadata.IsSecure)

Page 15: MEF

{ var messageSender = sender.Value; messageSender.Send("Server is fine"); break; } } }}

Using Weakly-typed metadataTo access metadata in a weakly-typed fashion, you import uisng the type System.Lazy<T, TMetadata> passing IDictionary<string,object> for the metadata. You can then access the metadata through the Metadata property which will be a dictionary.

Note: In general we recommend the strongly-typed method for accessing metadata, however there are systems that need to access the metadata in a dynamic fashion, which this allows.

[Export]public class HttpServerHealthMonitor{ [ImportMany] public Lazy<IMessageSender, IDictionary<string,object>>[] Senders { get; set; }

public void SendNotification() { foreach(var sender in Senders) { if (sender.Metadata.ContainsKey("Transport") && sender.Metadata["Transport"] == MessageTransport.Smtp && sender.Metadata.ContainsKey("Issecure") && Metadata["IsSecure"] == true) { var messageSender = sender.Value; messageSender.Send("Server is fine"); break; } } }}

Metadata filtering and DefaultValueAttributeWhen you specifiy a metadata view, an implicit filtering will occur to match only those exports which contain the metadata properties defined in the view. You can specify on the metadata view that a property is not required, by using the System.ComponentModel.DefaultValueAttribute. Below you can see where we have specified a default value of false on IsSecure. This means if a part exports IMessageSender, but does not supply IsSecure metadata, then it will still be matched.

Page 16: MEF

public interface IMessageSenderCapabilities{ MessageTransport Transport { get; } [DefaultValue(false)]; bool IsSecure { get; }}

CatalogsOne of value propositions of MEF's attributed programming model is the ability to dynamically discover parts via catalogs. Catalogs allow applications to easily consume exports that have self-registered themselves via the Export attribute. Below is a list the catalogs MEF provides out of the box.

Assembly CatalogTo discover all the exports in a given assembly one would use the [System.ComponentModel.Composition.Hosting.AssemblyCatalog].

var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());

Directory CatalogTo discover all the exports in all the assemblies in a directory one would use the [System.ComponentModel.Composition.Hosting.DirectoryCatalog].

var catalog = new DirectoryCatalog("Extensions");

If a relative directory is used it is relative to the base directory of the current AppDomain.

The DirectoryCatalog will do a one-time scan of the directory and will not automatically refresh when there are changes in the directory. However, you can implement your own scanning mechanism, and call Refresh() on the catalog to have it rescan. Once it rescans, recomposition will occur.

var catalog = new DirectoryCatalog("Extensions");//some scanning logiccatalog.Refresh();

Note: DirectoryCatalog is not supported in Silverlight.

Aggregate CatalogWhen AssemblyCatalog and DirectoryCatalog are not enough individually and a combination of catalogs is needed then an application can use an [System.ComponentModel.Composition.Hosting.AggregateCatalog]. An AggregateCatalog combines multiple catalogs into a single catalog. A common pattern is to add the current

Page 17: MEF

executing assembly, as well as a directory catalog of third-party extensions. You can pass in a collection of catalogs to the AggregateCatalog constructor or you can add directly to the Catalogs collection i.e. catalog.Catalogs.Add(...)

var catalog = new AggregateCatalog( new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()), new DirectoryCatalog("Extensions"));

Type CatalogTo discover all the exports in a specific set of types one would use a [System.ComponentModel.Composition.Hosting.TypeCatalog].

var catalog = new TypeCatalog(typeof(type1), typeof(type2), ...);

DeploymentCatalog - Silverlight onlyMEF in Silverlight includes the DeploymentCatalog for dynamically downloading remote XAPs. For more on that see the DeploymentCatalog topic.

Using catalog with a ContainerTo use a catalog with the container, simpy pass the catalog to the container's constructor.

var container = new CompositionContainer(catalog);

Filtering CatalogsWhen using child containers it may be important to filter catalogs based on some specific criteria. For example, it is common to filter based on part's creation policy. The following code snippet demonstrates how to set up this particular approach:

var catalog = new AssemblyCatalog(typeof(Program).Assembly);var parent = new CompositionContainer(catalog);

var filteredCat = new FilteredCatalog(catalog, def => def.Metadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName) && ((CreationPolicy)def.Metadata[CompositionConstants.PartCreationPolicyMetadataName]) == CreationPolicy.NonShared);var child = new CompositionContainer(filteredCat, parent);

var root = child.GetExportedObject<Root>();child.Dispose();

If CreationPolicy is not enough as a criteria to select parts you may want to use the

Page 18: MEF

[System.ComponentModel.Composition.PartMetadataAttribute] instead. It allows you to attach metadata to the part so you can use it to construct a filtering expression. For example, the following is a class that with the attribute applied:

[PartMetadata("scope", "webrequest"), Export]public class HomeController : Controller{}

This allows you to create a child container with parts that should be scoped to a (logical) web request. Note that it is up to you to define a scope boundary, in other words, MEF doesn't know what a "webrequest" is, so you have to create some infrastructure code to create/dispose containers per web request.

var catalog = new AssemblyCatalog(typeof(Program).Assembly);var parent = new CompositionContainer(catalog);

var filteredCat = new FilteredCatalog(catalog, def => def.Metadata.ContainsKey("scope") && def.Metadata["scope"].ToString() == "webrequest");var perRequest = new CompositionContainer(filteredCat, parent);

var controller = perRequest.GetExportedObject<HomeController>();perRequest.Dispose();

Note that we do not provide a FilteredCatalog class. A simple implementation that illustrates what would take to build one follows:

using System;using System.ComponentModel.Composition.Primitives;using System.ComponentModel.Composition.Hosting;using System.Linq;using System.Linq.Expressions;

public class FilteredCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged{ private readonly ComposablePartCatalog _inner; private readonly INotifyComposablePartCatalogChanged _innerNotifyChange; private readonly IQueryable<ComposablePartDefinition> _partsQuery;

public FilteredCatalog(ComposablePartCatalog inner, Expression<Func<ComposablePartDefinition, bool>> expression) { _inner = inner; _innerNotifyChange = inner as INotifyComposablePartCatalogChanged; _partsQuery = inner.Parts.Where(expression); }

public override IQueryable<ComposablePartDefinition> Parts

Page 19: MEF

{ get { return _partsQuery; } }

public event EventHandler<ComposablePartCatalogChangeEventArgs> Changed { add { if (_innerNotifyChange != null) _innerNotifyChange.Changed += value; } remove { if (_innerNotifyChange != null) _innerNotifyChange.Changed -= value; } }

public event EventHandler<ComposablePartCatalogChangeEventArgs> Changing { add { if (_innerNotifyChange != null) _innerNotifyChange.Changing += value; } remove { if (_innerNotifyChange != null) _innerNotifyChange.Changing -= value; } }}

Parts LifetimeIt’s very important that one understand a parts lifetime within a MEF container and its implications. Given that MEF focuses on open ended applications this becomes especially important as the application authors won’t have control over the set of parts once the app ships and third party extensions come to play. Lifetime can be explained as being the desired “shareability” of a part (transitively, its exports) which translates to the policy that controls when a new part is created as well as when the part will be shut down or disposed.

Shared, Non Shared and ownershipThe “shareability” of a part is defined through the CreationPolicy set (class level) using the PartCreationPolicyAttribute. The following values are supported:

Shared: the part author is telling MEF that at most one instance of the part may exist per container.

NonShared: the part author is telling MEF that each request for exports of the part will be served by a new instance of it.

Page 20: MEF

Any or not supplied value: the part author allows the part to be used as either “Shared” or “NonShared”.

The Creation Policy can be defined on a part using the [System.ComponentModel.Composition.PartCreationPolicyAttribute]:

[PartCreationPolicy(CreationPolicy.NonShared)][Export(typeof(IMessageSender))]public class SmtpSender : IMessageSender{}

The container will always have the ownership of parts it creates. In other words, the ownership is never transferred to an actor that requested it by using the container instance (directly) or through an import (indirectly). Imports can also define or constraint the creation policy of parts used to supply the import values. All you have to do is specify the CreationPolicy enum value for RequiredCreationPolicy:

[Export]public class Importer{ [Import(RequiredCreationPolicy=CreationPolicy.NonShared)] public Dependency Dep { get; set; }}

This is a useful for scenarios where the “shareability” of a part is relevant for the importer. By default, the RequiredCreationPolicy is set to Any, so Shared and NonShared parts can supply the values.The following table summarizes the behavior:

- Part.Any Part.Shar

ed Part.NonSha

red Import.Any Shared Shared Non Shared Import.Shared Shared Shared No Match Import.NonShared

Non Shared No Match Non Shared

Note that when both sides define “Any” the result will be a shared part.

Disposing the containerA container instance is generally the lifetime holder of parts. Part instances created by the container have their lifetime conditioned to the container’s lifetime. The way to signal the end of the container lifetime is by disposing it. The implications of disposing a container are: Parts that implement IDisposable will have the Dispose method called Reference to parts held on the container will be cleaned up Shared parts will be disposed and cleaned up Lazy exports won’t work after the container is disposed Operations might throw System.ObjectDisposedException

Page 21: MEF

Container and parts referencesWe believe that .Net Garbage Collector is the best thing to rely on for proper clean up. However, we also need to provider a container that has a deterministic behavior. Thus, the container will not hold references to parts it creates unless one of the following is true: The part is marked as Shared The part implements IDisposable One or more imports is configured to allow recomposition

For those cases, a part reference is kept. Combined with the fact that you can have non shared parts and keep requesting those from the container then memory demand can quickly become an issue. In order to mitigate this issue you should rely on one of the following strategies discussed in the next two topics.

Scoped operations and early reclaim of resourcesSome common kinds of applications like web apps and windows services vary greatly from desktop applications. They are more likely to rely on batched and short lived operations. For example, a windows service might watch a directly and once a pre-determined number of file is present, start a batching operation that transforms those files into another format. Web operations may be scoped by per-web-request operations.

For those scenarios you should either use child containers (explained in the next topic) or release early the object graph. The latter allows the container to dispose and clear out references to non shared parts in the object graph – until it reaches a shared part.

In order to early release the object graph you need to call the method ReleaseExport exposed by the CompositionContainer:

var batchProcessorExport = container.GetExport<IBatchProcessor>();

var batchProcessor = batchProcessorExport.Value;batchProcessor.Process();

container.ReleaseExport(batchProcessorExport);

The figure below depicts an object graph and show what parts would be released (references removed, disposed) and the ones that would be left untouched:

Page 22: MEF

As the root part is just non shared no reference was being kept by the container, so it is basically a no-operation. We proceed traversing the graph checking the exports served to the root part. Dep 1 is both non shared and disposable, so the part is disposed and its reference is removed from the container. The same happens with Dep 2, however, the export used by Dep is left untouched as it is shared – so other parts may be using it.

Note that the implementation traverses the graph in a depth-first way.

Container hierarchiesAnother way to approach the same problem is to use container hierarchies. You can create containers and connect them to a parent container, making them child containers. Note that unless you provide a different catalog to the child container, it wouldn’t be of much help as instantiation will continue to happen on the parent. Hence, what you should do is either filter the parent catalog based on a criterion that divides the set of parts that should be created on the parent container from those that should be created on the child, or you should specify a completely new catalog that expose a set of parts that should be created on the child container. As the child is expected to be short lived, parts created in it will be released and disposed earlier. A common approach is to have Shared parts created on the parent container and Non Shared

Page 23: MEF

on the child container. As Shared parts may depend on exports supplied by Non Shared, then the main catalog will have to contain the whole set of parts whereas the child container should have a filtered view of the main catalog with only the non shared parts.

For more information on this topic please check Filtering Catalogs

Disposal orderingDisposal ordering is not guaranteed in any way. That means that you should not try to use an import in your Dispose method. For example:

[Export]public class SomeService : IDisposable{ [Import] public ILogger Logger { get; set; } public void Dispose() { Logger.Info("Disposing"); // might throw exception! }}

Using the imported logger instance on your dispose method implementation may be a problem as the implementation of the ILogger contract may also be disposable, and as such may have been disposed already.

AddPart/RemovePartNot every part is created by the container. You can also add and remove parts from it. This process triggers composition and may start creating parts to satisfy dependencies of the part added recursively. When the part added is removed MEF is smart enough to reclaim the resources and dispose the non shared parts used by the part added.

Note: that MEF will never take ownership of an instance supplied by you, but it does have the ownership of part it creates to satisfy your instance’s imports.

Page 24: MEF

using System;using System.ComponentModel.Composition;using System.ComponentModel.Composition.Hosting;using System.ComponentModel.Composition.Primitives;

class Program{ static void Main(string[] args) { var catalog = new AssemblyCatalog(typeof(Program).Assembly); var container = new CompositionContainer(catalog); var root = new Root();

// add external part container.ComposeParts(root);

// ... use the composed root instance

// removes external part batch = new CompositionBatch(); batch.RemovePart(root); container.Compose(batch); }}

public class Root{ [Import(RequiredCreationPolicy = CreationPolicy.NonShared)] public NonSharedDependency Dep { get; set; }}

[Export, PartCreationPolicy(CreationPolicy.NonShared)]public class NonSharedDependency : IDisposable{ public NonSharedDependency() { }

public void Dispose() { Console.WriteLine("Disposed"); }}

RecompositionSome applications are designed to dynamically change at runtime. For example, a new extension may be downloaded, or others might become unavailable for a variety of reasons. MEF is prepared to handle these kinds of scenarios by relying on what we call recomposition, which is changing values of imports after the initial composition.

An import can inform MEF that it supports recomposition through the [System.ComponentModel.Composition.ImportAttribute] using the Allowrecomposition property. See the code snippet below:

Page 25: MEF

[Export]public class HttpServerHealthMonitor{ [ImportMany(AllowRecomposition=true)] public IMessageSender[] Senders { get; set; }

This tells MEF that your class is ready to handle recomposition, and if the availability of IMessageSender implementations changes (either a new one is available, or made unavailable), the collection should be changed to reflect it. Once a part has opted in for recomposition, it will get notified whenever there is a change to the implementations available in the catalog, or if instances have been manually added / removed from the container.

Caveats of Recomposition When recomposition occurs, we will replace the instance of the collection / array with a new

instance, we will not update the existing instance. In the example above, if a new IMessageSender appears, Senders will be completely replaced with a new array. This is in order to facilitate thread-safety.

Recomposition is valid for virtually all types of imports supported: fields, properties and collections, but it is not supported for constructor paramters.

If your type happens to implement the interface [System.ComponentModel.Composition.IPartImportsSatisifiedNotification], be aware that ImportCompleted will also be called whenever recomposition occurs.

Querying the CompositionContainerThe CompositionContainer exposes a handful of overloads to get exports and exported objects and collections of both.

You need to observe the following behavior shared among these methods overloads – unless noted otherwise.

When requesting a single instance, an exception will be thrown if none is found When requesting a single instance, an exception will be thrown if more than one is found

GetExportedValueIn the following code snippet we request an instance of Root (contract) instance.var container = new CompositionContainer(new AssemblyCatalog(typeof(Program).Assembly));Root partInstance = container.GetExportedValue<Root>();

If you have an export under a different contract name, you will need to use a different overload:

[Export("my_contract_name")]public class Root{}

Page 26: MEF

var container = new CompositionContainer(new AssemblyCatalog(typeof(Program).Assembly));Root partInstance = container.GetExportedValue<Root>("my_contract_name");

GetExportGetExport retrieves a lazily instantiated reference to an export. Accessing the Value property of the export will either force the export instance to be created. Successive invocation of the export's Value will return the same instance, regardless of whether the part has a Shared or Non-Shared lifetime.

Lazy<Root> export = container.GetExport<Root>();var root = export.Value; //create the instance.

GetExportedValueOrDefaultGetExportedValueOrDefault works exactly as GetExportedValue with the difference that it won’t throw an exception in case nothing matches.

var root = container.GetExportedValueOrDefault<Root>(); // may return null

Composition BatchA MEF container instance is not immutable. Changes can happen if the catalog supports changes (like watching a directory for changes) or if your code add or remove parts in runtime. Previously you had to make the changes and invoke the Compose method on the CompositionContainer. In the Preview 4 release we have introduced support for a composition batch.

The batch contains a list of parts to be added and/or removed. After performing the changes, the container automatically triggers a composition which updates recomposable imports affected by the changes.As a scenario, consider a settings window and a user selecting and deselecting options. Those would map to parts present or not on the container. To apply the batch, you would call the Compose method, as follows:

var batch = new CompositionBatch();batch.AddPart(partInstance1);batch.AddPart(partInstance2);batch.RemovePart(part3);

container.Compose(batch);

For types that actually use the attributed programming model there are some extension methods on AttributedModelServices for CompositionContainer that allow you to do hide the CompositionBatch in some common cases where it isn't needed.

Page 27: MEF

container.ComposeParts(partInstance1, partInstance2,... ); // creates a CompositionBatch and calls AddPart on all the passed parts followed by Composecontainer.ComposeExportedValue<IFoo>(instanceOfIFoo); // creates a CompositionBatch and calls AddExportedValue<T> followed by Compose.container.ComposeParts(partInstance1, partInstance2,...) ' creates a CompositionBatch and calls AddPart on all the passed parts followed by Composecontainer.ComposeExportedValue(Of IFoo)(instanceOfIFoo) ' creates a CompositionBatch and calls AddExportedValue<T> followed by Compose.

Diagnosing Composition ProblemsRejection-Related ProblemsOne of the implications of Stable Composition is that the rejected parts will simply never show up.

Because parts are interdependent, one broken part can cause a chain of other dependent parts to be rejected as well.

Finding the root cause of a ‘cascading rejection’ like this can be tricky.

Included in the samples under /Samples/CompositionDiagnostics is a prototype diagnostic library that can be used to track composition problems down.

The two basic functions that are implemented allow the rejection state of a part to be inspected, and the contents of an entire catalog to be dumped along with status and root cause analysis information.

Dump Composition State

Page 28: MEF

For comprehensive diagnostics, the complete composition can be dumped in text format:

The output contains many interesting things, including ‘primary rejection’ guesses and analysis of common problems like mismatched type identity, mismatched creation policy, and missing required metadata:

There’s enough information here to correctly diagnose most common issues.

Find Likely Root CausesThe dump technique above is comprehensive but verbose, and if you have access to the running process in a debugger, the following is more likely to be convenient:

The return value of CompositionInfo.GetPartDefinitionInfo() is an object that gives quick access

Page 29: MEF

to all of the same analytical information as the text dump, but relating to the part Foo. The API exposes:

* The part’s rejection state (IsRejected) * Whether it is a primary cause of rejection, or if it is rejected because of other cascading rejections (IsPrimaryRejection) * Which parts are likely to be the root causes of the part’s rejection (PossibleRootCauses) * The state of all imports (ImportDefinitions) * For each import, which exports would satisfy the imports (ImportDefinitions..Actuals) * For each import, which other exports with the same contract name were not matched, and the reason for each (ImportDefinitions..UnsuitableExportDefinitions)

Debugger ProxiesMEF types like ComposablePartCatalog can be inspected under the debugger:

The mefx Command-Line Analysis ToolIn Preview 7 and later there is an included utility that makes use of the diagnostics routines to print information about parts directly from the command-line.

C:\Users\...\CompositionDiagnostics> mefx /?

/?

Print usage.

/causes

List root causes - parts with errors not related to the rejection of other parts.

/dir:C:\MyApp\Parts

Specify directories to search for parts.

/exporters:MyContract

List exporters of the given contract.

/exports

Find exported contracts.

Page 30: MEF

/file:MyParts.dll

Specify assemblies to search for parts.

/importers:MyContract

List importers of the given contract.

/imports

Find imported contracts.

/parts

List all parts found in the source assemblies.

/rejected

List all rejected parts.

/type:MyNamespace.MyType

Print details of the given part type.

/verbose

Print verbose information on each part.

The /parts switch list all parts in a composition:

C:\Users\...\CompositionDiagnostics> mefx /dir:..\MefStudio /parts

Designers.CSharp.Commands

Designers.BasicComponentFactory

Designers.CSharpFormFactory

...

While the /rejected and /causes switches will print information about rejected parts and suspected root causes respectively.

By specifying the /verbose switch, detailed information about parts can be retrieved:

C:\Users\...\CompositionDiagnostics> mefx /dir:..\MefStudio /type:Designers.BasicComponentFactory /verbose

[Part] Designers.BasicComponentFactory from: DirectoryCatalog (Path="..\MefStudio")

Page 31: MEF

[Export] Designers.BasicComponentFactory (ContractName="Contracts.HostSurfaceFactory")

[Import] Contracts.HostSurfaceFactory.propertyGrid (ContractName="Contracts.IPropertyGrid")

[Actual] ToolWindows.PropertyGridWindow (ContractName="Contracts.IPropertyGrid") from: ToolWindows.PropertyGridWindow from: DirectoryCatalog (Path="..\MefStudio")

[Import] Contracts.HostSurfaceFactory.Commands (ContractName="System.Void(Contracts.HostSurface)")

[Actual] Designers.CSharp.Commands.Cut (ContractName="System.Void(Contracts.HostSurface)") from: Designers.CSharp.Commands from: DirectoryCatalog (Path="..\MefStudio")

[Actual] Designers.CSharp.Commands.Copy (ContractName="System.Void(Contracts.HostSurface)") from: Designers.CSharp.Commands from: DirectoryCatalog (Path="..\MefStudio")

[Actual] Designers.CSharp.Commands.Paste (ContractName="System.Void(Contracts.HostSurface)") from: Designers.CSharp.Commands from: DirectoryCatalog (Path="..\MefStudio")

It is important to realise that the utility can only analyze MEF assemblies built against a compatible version of MEF; for example, mefx.exe built against the CodePlex drops will not be able to analyze assemblies built against the signed .NET Framework version of MEF, and vice-versa.

TracingDiagnostic information produced during composition can be viewed in the Output window of the debugger, or by attaching to the "System.ComponentModel.Composition" trace source.