Udostępnij za pośrednictwem


What’s new in MEF 2 Preview 4? [Alok, Nick]

The MEF team has been quietly at work for some time getting the finishing touches on another developer preview. You can download the new source and .NET 4.0-compatible binaries from the Codeplex site.

This preview is another step towards the next supported release, which will be made as part of .NET 4.5.

Highlights of the new version are:

  • Support for generic parts
  • A new convention-based registration API (attributes optional!)
  • New scoping features that use ExportFactory and CompositionScopeDefinition
  • Integrated support for MEF composition in ASP.NET MVC
  • Diagnostics improvements

Open generic parts

Open generic types can now be used as parts. For example, if you are using the Generic Repository pattern, you may use IRepository<T> as a contract:

 public interface IRepository<T> where T : class
{
    T FindById(int id);
}

Other parts then import this contract using closed types like IRepository<Customer> :

 [Export]
public class CustomersWindow :  Window
{
    [ImportingConstructor]
    public CustomerWindow(IRepository<Customer> customers)
    {
        // … use customers

Rather than create a specific CustomerRepository class, and a similar implementation for other kinds of repository, the repository interface can be exported on a generic type:

 [Export(typeof(IRepository<>))]
public class EntityFrameworkRepository<T> : IRepository<T> where T : class
{
    // … generic repository implementation

Open generic parts reduce the effort required to implement many common scenarios.

Convention-based part registration

The previous MEF release was optimized for third-party extensibility scenarios, where explicit annotation of parts with attributes makes a lot of sense. When the entire application is under the control of one group of developers, applying attributes to describe parts can be redundant and repetitive.

For example, every class that derives from Window in an application might have an Export attribute applied:

 [Export]
class CustomersWindow : Window { … }

RegistrationBuilder is a class added in the new release that allows a set of rules (‘conventions’) to be defined that map types to parts, without the need for attributes.

 var rb = new RegistrationBuilder();
rb.ForTypesDerivedFrom<Window>().Export();

The standard MEF catalogs accept a RegistrationBuilder as a constructor parameter, and will apply the rules defined in the registration builder to the types included in the catalog:

 var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), rb);

RegistrationBuilder is available in System.ComponentModel.Composition.Registration.

Composition scoping enhancements

In the first version of MEF, part instances could be either shared or per-importer, but it was much harder to control scope and lifetime in a more fine-grained way. Often it is desirable to configure and manage parts as a group to handle some kind of unit of work – a web request, processing a message or managing a single visual element like a WPF view and ViewModel.

The new MEF release makes it easier to control the lifetime and sharing of parts. This example shows how the new scoping support can be used to isolate the parts used for processing 'messages' — the basic pattern applies to many scenarios.

 [Export]
public class DataConnection
{
}
 
[Export]
public class MessageHandler
{
    [ImportingConstructor]
    public MessageHandler(DataConnection dataConnection) { … }

    // HandleMessage(Message m) omitted
}

The two 'skeleton' parts above work together to process a network message of some kind. Each time a message handler is created, it will be initialized with a DataConnection that it will use to make changes in response to the message.

More importantly, other dependencies of MessageHandler (not shown) should share the same DataConnection. MessageHandlers processing different messages should get different DataConnections. Any of these parts may also have dependencies that are global, i.e. common to all scopes.

 [Export]
public class MessageListener
{
    [ImportingConstructor]
    public MessageListener(ExportFactory<MessageHandler> handlerFactory) { … }
 
    public void ProcessRequest()
    {
        var message = GetNextMessage();
        using (var handler = _handlerFactory.CreateExport())
            handler.HandleMessage(message);
    }
}

The implementation of MessageListener may be familiar to users of MEF in Silverlight 4. The ExportFactory<T> type allows the MessageListener to create a new message handler for each message.

Now, by default, this won’t enable any kind of scoping around the individual message listeners — they’ll all share the same scope (and DataConnection) as the MessageListener.

To describe the scoping we want, the container needs to be created with a CompositionScopeDefinition in place of a ComposablePartCatalog:

 var globalParts = new TypeCatalog(typeof(MessageListener));
var perMessageParts = new TypeCatalog(typeof(MessageHandler), typeof(DbConnection));
var scopeDefinition = new CompositionScopeDefinition(globalParts,
            new[] { new CompositionScopeDefinition(perMessageParts, null)});

var container = new CompositionContainer(scopeDefinition);
var listener = container.GetExportedValue<GlobalRequestManager>();
listener.HandleMessage();
listener.HandleMessage();

Every time HandleMessage() is called, new scoped instances of MessageHandler and DataConnection are created and released.

CompositionScopeDefinition is available in the System.ComponentModel.Composition.Hosting namespace.

Composition provider for ASP.NET MVC

Simplifying the use of MEF in web applications is a core goal of the new release. To further this aim, we are supplying functionality that integrates MEF into ASP.NET MVC, to allow controllers and other MVC types to participate in composition.

To use this feature, reference the System.ComponentModel.Composition.Web.Mvc assembly in your ASP.NET MVC project. All configuration is handled automatically — to create parts, add them to a Parts namespace (folder) under the MVC site:

 namespace FabrikamBooks.Parts
{
    class TraceLogger : ILogger { … }

Just by being under the Parts folder, the class will automatically ‘export’ all of its implemented interfaces. The longest constructor on the class will be treated as its importing constructor. No attributes are required, unless you want to override the inbuilt defaults.

Controllers simply take dependencies on parts through their constructors:

 class HomeController : Controller
{
    // MEF will automatically supply TraceLogger
    public HomeController(ILogger logger) { … }

The composition provider implements a set of defaults that we feel are sensible for most web applications. These defaults can be overridden by applying attributes to parts. Advanced users can still set up MEF ‘by hand’ to meet more specific goals.

The downloadable binaries for this feature are built against ASP.NET MVC version 3; if you want to rebuild the assemblies from source, you'll need to get ASP.NET MVC separately. To maintain a flexible release schedule as this component evolves, we anticipate delivering binaries via NuGet rather than the .NET Framework SDK.

Diagnostics improvements

Providing good diagnostic support in a framework as dynamic and flexible as MEF is a challenge, and is something that we expect to go on improving over time.

In this release, two key improvements have been made to diagnostics in MEF.

CompositionOptions.DisableSilentRejection

MEF’s roots in extensibility make it more tolerant of composition problems than other similar frameworks. Parts with missing dependencies are identified and excluded from composition, which makes diagnostics hard in some circumstances.

When constructing a CompositionContainer in the new MEF version, flags can be passed controlling this and other behaviors. Specifying CompositionOptions.DisableSilentRejection will cause exceptions to be thrown when any composition problem occurs.

 var container = new CompositionContainer(
    catalog, CompositionOptions.DisableSilentRejection);

It is recommended that most users building non-third-party-extensible applications specify this flag. It is set by default when using the ASP.NET MVC composition provider described previously.

Better exception messages

Finally, the exception message text in the first version of MEF can be complex and verbose in some situations. The new release improves the way exception messages are formatted, presenting the most important information up-front.

Moving forward

Subscribe to this blog for examples and more detailed documentation on the new features.

We’re looking forward to your feedback on this release. As always, you can get in touch with the team through this blog or at the MEF Codeplex site’s issue list and discussion forum.

Comments