Before I show you the cooperation between the Autofac and Microsoft Extensibility Framework (MEF) I want bring you in short my opinion why is there the place for both of them. If you are try to search around, you will find lot of posts which describes the differences between the MEF or Autofac, also, you will find lot of posts which advocates to use MEF as IoC Container, or, on other side, to use Autofac as replacement of the MEF.
Autofac vs. Microsoft Extensibility Framework
Look at the naming: Microsoft Extensibility Framework. The important word here is: Framework. This isn’t just some buzzword but word which expresses what the MEF is and what is providing for you. Wikipedia’s definition of framework is pretty common and incomplete so I introduce other one: Framework “do not simply supply individual components (or individual classes) but also provide the interconnections among the components they supply.”[1]
To understand this we should explain the term component as opposite to the term framework[1]:
- it can be compiled as a stand-alone unit
- it exposes one or more interfaces
- it provides an implementation for the interfaces it exposes
- it is seen by its clients only through the interfaces it exposes
The framework is neither only set of components nor it is working application. This makes describing a framework so complicated. I agree to understand a framework as “a set of related abstract interfaces, design patterns and concrete components that are designed to be used with each other to facilitate the development of applications in a particular domain.”[1] The theory is not so clear as should.
The important is that we found differences between the MEF as a framework on one and the Autofac as a component on other side. MEF as a framework isn’t working application and exposes not only components but also the design patterns. Through the hot-spots, points where customization takes places[1] have we possibility to plug-in own specific domain code.
Framework Customization Code in a Framework[1]
Perhaps because the Dependency Injection Control can be seen as a key feature[1] of frameworks is it often confusing to distinct between the components like Autofac and MEF like a framework.
The final quote: yes, you can use Autofac as replacement for the MEF or vice versa, nobody can stop you from doing it. But you should ask yourself if your are using the right thing for your purpose. Each of them have reason to be here, each of them was build for special purpose. There is no Autofac vs. MEF. There is no war. There is Autofac and/or MEF. But I don’t want to go deeper here, instead of that, I show you how they can live together, more or less.
Sample Scenario
The scenario is simple. The application is executing n plugins. Each plugin have own business logic, which is invoked in the host application.
Bootstrapping
Thank to David Buksbaum’s blog post for the inspiration. In really, is this posts adaption of his idea.
The Bootstrapper class encapsulates the application initialization. In the method InitializeApplication is initialized MEF as well as Autofac.
namespace AutofacMexSample.Bootstrap { public class Bootstrapper : IBootstrapper { [ImportMany(typeof(IModule))] private IEnumerable _pluginsAutofacModules; private AggregateCatalog _aggregateCatalog; public Bootstrapper(IEnumerable pluginPaths) { IEnumerable directoryPluginCatalogs = pluginPaths.Select(pluginPath => new DirectoryCatalog(pluginPath)); _aggregateCatalog = new AggregateCatalog(directoryPluginCatalogs); } public void InitializeApplication() { InitializeMex(); InitializeAutofac(); } private void InitializeAutofac() { var autofacContainerBuilder = new ContainerBuilder(); RegisterPluginsAutofacModules(autofacContainerBuilder); autofacContainerBuilder.RegisterModule(new HostApplicationAutofacModule()); autofacContainerBuilder.RegisterComposablePartCatalog(_aggregateCatalog, new TypedService(typeof(IPluginBusinessLayer))); AutofacContainer = autofacContainerBuilder.Build(); } public IContainer AutofacContainer { get; private set; } private void RegisterPluginsAutofacModules(ContainerBuilder autofacContainerBuilder) { foreach (var pluginAutofacModule in _pluginsAutofacModules) { autofacContainerBuilder.RegisterModule(pluginAutofacModule); } } private void InitializeMex() { var compositionContainer = new CompositionContainer(_aggregateCatalog); compositionContainer.ComposeParts(this); } } }
There is the one side where host application provide components (described in the module HostApplicationAutofacModule) for self and all other plugins, for example the instance of ILogger, which is used by all plugins. Autofac.Module class is the base class for user-defined modules where the component registration is performed.
namespace AutofacMexSample.Bootstrap.Autofac { public class HostApplicationAutofacModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType().As().InstancePerLifetimeScope(); ; builder.RegisterType().As().Exported(configurationBuilder => configurationBuilder.As()).SingleInstance(); } } }
The only things which I’m importing by initialization of MEF, in the method HostApplicationAutofacModule are instances of type IModule. IModule is the interface which is inherited by Autofac.Module class. Ok, ok I’m not so fair here. At this time (word ‘this’ is damn important in this context) I’m importing by MEF only things necessary to get the job done by Autofac later. But I’m also giving the Autofac the information and thereby order to care about the instantiation of other MEF components of type IPluginBusinesLayer (the method call RegisterComposablePartCatalog). As Autofac takes the role of emperor over the management of instances has he all necessary information about how to instantiate the objects, as well as about they lifetime.
namespace PluginFirst.Bootstrap.Autofac { [Export(typeof (IModule))] public class AutofacModule : Module { protected override void Load(ContainerBuilder builder) { RegisterComponentsUsedOutOfPluginBoundaries(builder); RegisterComponentsUsedOnlyByPlugin(builder); } private void RegisterComponentsUsedOnlyByPlugin(ContainerBuilder builder) { builder.RegisterType().As().InstancePerLifetimeScope(); } private void RegisterComponentsUsedOutOfPluginBoundaries(ContainerBuilder builder) { builder.RegisterType() .Exported(configurationBuilder => configurationBuilder.As()).InstancePerLifetimeScope(); builder.RegisterType() .As() .Exported(configurationBuilder => configurationBuilder.As()).InstancePerLifetimeScope(); } } }
The code above is the PluginFirst’s module, where the plugin components registration take place. Because each plugin can have a components used only by self, it is the responsibility of plugin self to register these: when if this were the case, than the plugin muss provide at least one implementation of class Autofac.Method and the class should by marked by attribute Export.
For better readability I split the registration in the two private methods. In the method RegisterComponentsUsedOnlyByPlugin are registered components used only by plugin self. On other side are components which must be known by application which is invoking the plugins. Those are registered in the method RegisterComponentsUsedOutOfPluginBoundaries. You should be aware of extension method Exported. This extension method is provided by Autofac MEF Integration. Components registered as Exported are available in the host application which is composing and invoking the plugins. The great about the Autofac MEF Integration is that we can now use the business layer of all plugins on the same way as any other registered component.
Invoking the Plug-ins
The OperatorOfPlugins is class where the invoking of business layers (implemented by each plugin) take place.
namespace AutofacMexSample { internal class Program { private static void Main(string[] args) { var pluginsPaths = ComposePluginsPaths(); var bootstrapper = new Bootstrapper(pluginsPaths); bootstrapper.InitializeApplication(); var autofacContainer = bootstrapper.AutofacContainer; var operatorOfPlugins = autofacContainer.Resolve(); operatorOfPlugins.RunPlugins(); Console.ReadKey(); } private static string[] ComposePluginsPaths() { string pluginBasePath = String.Format(@"{0}\Plugins\", AppDomain.CurrentDomain.BaseDirectory); var pluginsPaths = Directory.GetDirectories(pluginBasePath); return pluginsPaths; } } }
After the proper Autofac initialization can I use MEF components like any other registered component.
namespace AutofacMexSample.BusinessLayer { public class OperatorOfPlugins : IOperatorOfPlugins { private readonly IEnumerable _businessLayerOfRegisteredPlugins; public OperatorOfPlugins(IEnumerable businessLayerOfRegisteredPlugins) { if (businessLayerOfRegisteredPlugins == null) { throw new ArgumentNullException("businessLayerOfRegisteredPlugins"); } _businessLayerOfRegisteredPlugins = businessLayerOfRegisteredPlugins; } public void RunPlugins() { Console.WriteLine("Executing business layer of all plugins."); foreach (var businessLayerPlugin in _businessLayerOfRegisteredPlugins) { businessLayerPlugin.DoWork(); } } } }
Because you could have more than one plugin registered, takes the constructor parameter of type IEnumerable<IPluginBusinessLayer>. IPluginBusinessLayer is exactly the same type as we are registering in the PluginFirst’s AutofacModule as Exported. The nice thing about this is that the Autofac is injecting the Constructor automagically. But wait! You registered in the module not only the IPluginBusinessLayer but also the IStoreRepository! Is this one needed it in the host application? Yes and no…
namespace PluginFirst { [Export(typeof (IPluginBusinessLayer))] public class PluginBusinessLayer : IPluginBusinessLayer { private readonly IStoreRepository _storeRepository; private readonly ILogger _logger; [ImportingConstructor] public PluginBusinessLayer(IStoreRepository storeRepository, ILogger logger) { if (storeRepository == null) { throw new ArgumentNullException(&quot;storeRepository&quot;); } if (logger == null) { throw new ArgumentNullException(&quot;logger&quot;); } _storeRepository = storeRepository; _logger = logger; } public void DoWork() { Console.WriteLine(&quot;Executing business layer of PluginFirst.&quot;); IEnumerable data = _storeRepository.GetData(); _logger.Log(&quot;Start to getting data&quot;); foreach (var dataEntry in data) { Console.WriteLine(dataEntry); } _logger.Log(&quot;Getting data done&quot;); } } }
The PluginBusinessLayer is awaiting, among others, the object of type IStoreRepository and because registration of PluginBusinessLayer was within the RegisterComposablePartCatalog method, everything what is injected in must by either registered as Exported or registered in the host application.
You can see the the complete solution component architecture view on the picture below.
The complete solution can be downloaded here.
[1] Alessandro Pasetti: Software Frameworks and Embedded Control Systems