Introduction
MEF was probably one of the best additions to make its
appearance into the .net framework. It brought about a very simplistic way of
providing loosely coupled architecture to your application, by way of an
attribute driven design. That being said, I can certainly go on about the many
advantages MEF provides, but many people have already written extensively about
all its strengths. Instead I’m going to rather discuss how we can harness the
power of MEF and extend it by way of generics. We all use generics to make our
applications code…well more generic, so the question is why Inversion of
control containers can’t support it as well? Well lucky for us MEF can. But not
directly out of the box though, you have to go about setting up a few things
first to get it to fully support generic exports. Lucky for all the beginners
out there, I’m going to show you how to do this.
Getting Started
Let’s kick things off, firstly those of you who haven’t
used MEF and are thinking about integrating this technology into your
application. Please take a look at the following links below to give you some
clarity on this article that will follow.
Design Overview
Once you’ve caught up to speed. We can get to the good
part and discuss how all this works exactly. MEF supports certain types of
catalogs as you know for example we get the AggregateCatalog
, DirectoryCatalog
and finally the AssemblyCatalog
. Which we all know is highly useful in part
discovery by adding assemblies anyway you see fit. But it doesn’t help when it
comes to generics. It’s simply because the current catalogs seem a bit lack
luster when it comes to finding generic exports and knowing what to do with
them. That’s where the brilliant developers’ from the MEF Contrib team come
into the picture. They’ve developed a catalog that helps to map generic exports
to their concrete types, so you can later import them at run-time like any
other import. There is one slight problem though on their page on codeplex. And
that is the documentation doesn’t exactly give you the slightest idea how to
set it up in a simple way. Some might find it a bit confusing. If you are
however a diligent researcher or a google wizard you can certainly find the
solution. But this article isn’t directed at solving a problem, which has been
done already. This article is meant to save you time as well as help you set it
all up. Just if you are wondering the Mefcontrib dlls and all the files you
need are supplied in the source code above. A quick note if you do want the
latest copy of the library you can go to http://mefcontrib.codeplex.com/. Let’s begin!
Referencing the References
Above you can see all the necessary dependencies for
generic exports to be used. The referenced assemblies are as follows:
MefContrib.dll , System.Composition.dll
and my custom dll Mef.Extended.Tools
.
Using the code
Let’s start with the coding aspect of this guide. The
simple part of it all setting up the Catalog as well as the container for our
generic exports. Firstly in your applications startup add the following
references you added to your project earlier, so that you can have access to
the container, catalog, registry etc.
using System.ComponentModel.Composition.Hosting;
using MefContrib.Hosting.Generics;
using Mef.Extended.Tools.Generics;
class Program
{
static void Main(string[] args)
{
string assemblyPath = "ConsoleMef"; var genericRegistry = new GenericContractRegistry(
new TypeResolver(x => Path.GetFileName(x).StartsWith(assemblyPath)));
var catalog = new GenericCatalog(genericRegistry);
var container = new CompositionContainer(catalog);
}
}
As I’ve showed above, you start by instantiating a GenericContractRegistry
which takes my own custom class TypeResolver
as an argument. The TypeResolver’s
constructor accepts a lambda expression is used to filter all the assemblies
that contain exports in them. The GenericContractRegistry
is then passed in as
an argument to an instance of the GenericCatalog
. Which you’ll see shortly does
all that magic I was talking about earlier. The container then accepts the
catalog as its argument to then compose all exports that are available to later
be imported. Those of you who are a little lost about my custom type, well
don’t stress they will be discussed in depth shortly.
Applying some Exportation
Now that the container is setup to discover exports. You
now need to supply the exports that are going to be used throughout the life
time of your application. Adding exports is exactly the same as what you’ve
been doing with all your previous MEF implementations. it’s the same as before.
For example
[InheritedExport]
public interface IRepository<T> where T : class
{
string GetName(T obj);
}
One thing to note at time of writing my implementation
only supports the use of InheritedExportAttribute
. The reason for this is it
makes more sense to mark an interface or abstract class with an attribute to be
exported rather than the implemented object, purely because of the fact if you
on a big development team like me, a simple thing like marking a class with an
attribute can easily be forgotten. Note this will also work for generic
abstract classes.
Here’s an example repository that Implements our exported
interface. Note both have to be generic types.
public class PersonRepository<T> : IRepository<t>
where T : class
{
public string GetName(T obj)
{
Console.WriteLine("calling GetName");
Console.WriteLine(obj.ToString());
return obj.ToString();
}
}
class Program
{
[Import]
public IRepository<Person> Repository { get; set; }
static void Main(string[] args)
{
string assemblyPath = "ConsoleMef"; var genericRegistry = new GenericContractRegistry(
new TypeResolver(x => Path.GetFileName(x).StartsWith(assemblyPath)));
var catalog = new GenericCatalog(genericRegistry);
var container = new CompositionContainer(catalog);
new Program().Run(container);
Console.ReadKey();
}
void Run(CompositionContainer container)
{
container.ComposeParts(this);
RunTest();
}
void RunTest()
{
var p = new Person { FirstName = "Dean", LastName = "Oliver" };
Repository.GetName(p);
}
}
Show Me Some Generic Magic
And there we go it’s all working together in harmony, a
working implementation of a generic Export/Import
through mef. This has all
been done with marginal effort and even less code. After all one of mef’s
biggest strengths is its simplicity.
Now let’s examine under the hood what’s driving this
little generic engine.
Under The Hood
There are many facets that contribute to mef resolving
generic types for imports, but to keep with the theme of simplicity. Let me
start by saying that it’s really just one big mapping system that assigns an
interface to its implemented concrete type.
[Export(typeof(IGenericContractRegistry))]
public class GenericContractRegistry : GenericContractRegistryBase
{
private readonly ITypeResolver _resolver;
public GenericContractRegistry(ITypeResolver resolver)
{
_resolver = resolver;
}
protected override void Initialize()
{
RegisterAll(i => i.IsGenericType && i.GetCustomAttributes(false)
.Any(x => x.GetType() == typeof(InheritedExportAttribute)));
}
private void RegisterAll(Func<type,> filter)
{
var assemblyResolver = new AssemblyResolver(_resolver.AssemblyFilter);
var asm = assemblyResolver.Assemblies.GetMappings(x => _resolver.Get(x), filter)
foreach (var map in asm)
Register(map.Value, map.Key);
}
}
This class is the core to mef being able to support
generics. Think of it as one big generic gps. What it does is map an interface
that has the InheritedExportAttribute
applied to it, to a concrete implementation
of that specific type. It searches through the assemblies that the TypeResolver
defines based on particular criteria. And it adds it to a custom collection
called AssemblyList
. The AssemblyResolver
calls this list to get a particular
mapping from each assembly in the collection and then adds it to the register
so that MEF is aware of a new export in the GenericCatalog
. You may have
noticed in the MefContrib documentation they tell you to setup a mapping like what
I have commented out above. The problem with setting up a mapping this way is
it continually violates the open/closed principle. Which stipulates a class may
be open for extension but closed for modification. I see this as continually
modifying a class to support more mappings. So I thought of a more dynamic way
of doing this as I’ve explained above. Mef prides itself on part discovery, for
example its DirectoryCatalog
looks for parts in directories. So the question is
why we can’t be just as dynamic for our GenericCatalog
. It sometimes seems the
best solution is one that is more dynamic more often than not. That was my
rationale behind that little change. Let’s now turn our attention to retrieving
the assemblies that contain the mappings.
Off To the Libraries
public class AssemblyResolver
{
public AssemblyResolver(params Assembly[] assemblies) : this(() => assemblies) { }
public AssemblyResolver(Func<Assembly[]> getList)
{
Assemblies = new AssemblyList(getList());
}
public AssemblyResolver(Func<string,> searchFilter = null)
{
Assemblies = new AssemblyList(GetAssemblies(searchFilter));
}
public AssemblyList Assemblies { get; set; }
public static Assembly[] GetReferencedAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
public Assembly[] GetAssemblies([Optional]Func<string,> searchFilter)
{
var assemblies = Directory
.GetFileSystemEntries
(AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.AllDirectories);
var currentAssemblies =
searchFilter == null ? assemblies : assemblies.Where(searchFilter).ToArray();
var asm = currentAssemblies.Select(Assembly.LoadFrom);
return asm.Union(new[] { Assembly.GetExecutingAssembly() }).ToArray();
}
}
The AssemblyResolver
class gets all the assemblies that are
referenced in the current application as well as the current applications
executing assembly. This allows the GenericContractRegistry
to do an extensive
mapping of all generic exports across all assemblies involved. The search is
narrowed somewhat by the delegate that defines the search criteria for each
assembly, so only the assemblies that meet that particular criteria are
retrieved.
public Dictionary<Type, Type> GetMappings(Func<type,> resolver, Func<type,> filter)
{
return this.Select(assembly => (from c in assembly.GetTypes()
from i in c.GetInterfaces()
where filter(i)
select new
{
ClassType = c.GetInterfaceMap(i).TargetType,
InterfaceType = resolver(i)
}))
.SelectMany(genericExportList => genericExportList)
.ToDictionary(x => x.ClassType, z => z.InterfaceType);
}
LINQ Marks the spot
The true marvels of modern c#, where would we be without
linq? It makes querying through reflection an absolute treat. The GetMapping()
method creates a dictionary of class types as keys and interfaces that they
implement as values. This simple query associates all interfaces to their
correct implemenations. Above you can see the AssemblyList
collection that
takes care of holding all the applications assemblies in a custom assembly
collection.
That's All Folks
I’m not quite done with you avid developers just yet.
Sometimes complex things can be solved with simple ideas, what we achieved was
a mef container that completely supports generics and conforms to the mef way
of doing things. For those of you who enjoy even more of the Nitti gritty and
want to understand how the GenericCatalog
works to allow us to have generic
exports, well then I suggest taking a look at this article: http://mefcontrib.codeplex.com/wikipage?title=Generic%20Catalog&referringTitle=Documentation%20%26%20Features Well it’s been an absolute blast to write
this article, it is always easier when you love what you doing though. Hope you
find something useful out of this article. Please remember to vote and comment
if you don’t like something or a suggestion. At the end of the day criticism
just helps you to better understand your flaws so you can come closer to
perfecting them and making them your strengths.
History
Version 0.1: First release. Copyrights Dean Oliver.