Click here to Skip to main content
Click here to Skip to main content

MEF Generics

, 2 Feb 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
How we can harness the power of MEF and extend it by way of generics.

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

diagram.jpg

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

references.jpg

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"; //my example assembly
        //declare mefcontainer and add genericCatalog
        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"; //my example assembly
        //declare mefcontainer and add genericCatalog
        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

apprunnning.jpg

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.

/// <summary>
/// Maps concrete generic types to interface generic types.
/// 
[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)));
        //Register(typeof(IItemObservable<>), typeof(ItemObservableCollection<>));
        //Register(typeof(ICommandInvoker<>), typeof(CommandInvoker<>));
    }

    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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dean Oliver
Software Developer BBD Johannesburg
South Africa South Africa
Bsc (Hons) Business Information Systems.
MCTS: Web Applications Development with Microsoft .NET Framework 4
MCTS: Windows Communication Foundation Development with Microsoft .NET Framework 4
MCTS: Accessing Data with Microsoft .NET Framework 4
Microsoft Certified Professional Developer Certification.

Comments and Discussions

 
QuestionNice but, MEF 2 already supports generics, why not just use that [modified] PinmvpSacha Barber2-Feb-12 22:42 
AnswerRe: Nice but, MEF 2 already supports generics, why not just use that PinmemberDean Oliver2-Feb-12 23:03 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmvpSacha Barber2-Feb-12 23:05 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmemberDean Oliver3-Feb-12 0:47 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmvpSacha Barber3-Feb-12 1:31 
QuestionRe: Nice but, MEF 2 already supports generics, why not just use that PinmemberDean Oliver8-Mar-12 4:36 
AnswerRe: Nice but, MEF 2 already supports generics, why not just use that [modified] PinmvpSacha Barber8-Mar-12 5:21 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmemberDean Oliver8-Mar-12 5:23 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmvpSacha Barber8-Mar-12 5:44 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmvpSacha Barber8-Mar-12 5:46 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmvpSacha Barber8-Mar-12 6:14 
GeneralRe: Nice but, MEF 2 already supports generics, why not just use that PinmemberRob Grainger15-Aug-14 0:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 2 Feb 2012
Article Copyright 2012 by Dean Oliver
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid