Managed Extensibility Framework
Remarks#
One of MEF’s big advantages over other technologies that support the inversion-of-control pattern is that it supports resolving dependencies that are not known at design-time, without needing much (if any) configuration.
All examples require a reference to the System.ComponentModel.Composition assembly.
Also, all the (Basic) examples use these as their sample business objects:
using System.Collections.ObjectModel;
namespace Demo
{
public sealed class User
{
public User(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; }
public string Name { get; }
public override string ToString() => $"User[Id: {this.Id}, Name={this.Name}]";
}
public interface IUserProvider
{
ReadOnlyCollection<User> GetAllUsers();
}
}
Exporting a Type (Basic)
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
namespace Demo
{
[Export(typeof(IUserProvider))]
public sealed class UserProvider : IUserProvider
{
public ReadOnlyCollection<User> GetAllUsers()
{
return new List<User>
{
new User(0, "admin"),
new User(1, "Dennis"),
new User(2, "Samantha"),
}.AsReadOnly();
}
}
}
This could be defined virtually anywhere; all that matters is that the application knows where to look for it (via the ComposablePartCatalogs it creates).
Importing (Basic)
using System;
using System.ComponentModel.Composition;
namespace Demo
{
public sealed class UserWriter
{
[Import(typeof(IUserProvider))]
private IUserProvider userProvider;
public void PrintAllUsers()
{
foreach (User user in this.userProvider.GetAllUsers())
{
Console.WriteLine(user);
}
}
}
}
This is a type that has a dependency on an IUserProvider
, which could be defined anywhere. Like the previous example, all that matters is that the application knows where to look for the matching export (via the ComposablePartCatalogs it creates).
Connecting (Basic)
See the other (Basic) examples above.
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace Demo
{
public static class Program
{
public static void Main()
{
using (var catalog = new ApplicationCatalog())
using (var exportProvider = new CatalogExportProvider(catalog))
using (var container = new CompositionContainer(exportProvider))
{
exportProvider.SourceProvider = container;
UserWriter writer = new UserWriter();
// at this point, writer's userProvider field is null
container.ComposeParts(writer);
// now, it should be non-null (or an exception will be thrown).
writer.PrintAllUsers();
}
}
}
}
As long as something in the application’s assembly search path has [Export(typeof(IUserProvider))]
, UserWriter
’s corresponding import will be satisfied and the users will be printed.
Other types of catalogs (e.g., DirectoryCatalog
) can be used instead of (or in addition to) ApplicationCatalog
, to look in other places for exports that satisfy the imports.