Search code examples
c#mef

MEF -- I seem to be missing something here


So, I'm playing around with MEF -- kind of trying a classic IoC thing -- injecting a logging class. I have it working, but it is not working the way I expected it to work. Can you tell me where I'm off track?

I think that my problem is that I'm bootstrapping MEF incorrectly. I expected to Export a logger, and Import it to inject it into any class that wanted to use it.

What I'm finding is that for any class that wants to use it, I have to new up a container, load the catalogs, and call ComposeParts(). (which is actually a lot harder than just calling MyEventLogFactory.GetEventLog()). (I expected to have to bootstrap MEF once, on program init, but I seem to have to do it for every class that has in import)

Here's my bootstrap code (in the startup of a Console app):


static void Main(string[] args)
{
    InitMefContainer();
    var test = new TestHarness();
    test.RunTest();

    Console.ReadLine();
}

private static void InitMefContainer()
{
    var catalog = new AggregateCatalog();
    catalog.Catalogs.Add(new AssemblyCatalog(typeof(EventLogFactory).Assembly));
    catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
    var container = new CompositionContainer(catalog);
    container.ComposeParts();
    _Container = container;
}

Then my TestHarness class looks like this:


public class TestHarness
{
  [Import(typeof(IEventLog))]
  public IEventLog EventLog { get; set; }

  public void RunTest()
  {
     // did not expect to need to do this, but EventLog is null if I don't
     Program._Container.ComposeParts(this);

     this.EventLog.Write(some test event stuff);
  }
}

My problem is that EventLog is always null. -- Unless, I repeat all of the MefInitialization code that Program.Main executed (or, if I make the container that Program created a public static, I can call _Container.ComposeParts(this);

So, my question: what is the right way to bootstrap MEF so that it satisfies all of my imports? Is it common practice to have a public static container and call Container.Compose(this)? (or to use AStaticContainer.GetExportedValue, sort of like you would use ServiceLocator.GetService()? inside all of my classes -- I definitely have not seen that in any sample code out there.


Solution

  • I try to have as much as possible injected by the container in the bootstrap.

    I think something like this:

    static void Main(string[] args)
    {
        InitMefContainer();
    
        var test = _Container.GetExportedValue<TestHarness>();
        test.RunTest();
    
        Console.ReadLine();
    }
    
    private static void InitMefContainer()
    {
        var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new AssemblyCatalog(typeof(EventLogFactory).Assembly));
        catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
        var container = new CompositionContainer(catalog);
        container.ComposeParts();
        _Container = container;
    }
    
    [Export]
    public class TestHarness
    {
      [Import(typeof(IEventLog))]
      public IEventLog EventLog { get; set; }
    
      public void RunTest()
      {
         // did not expect to need to do this, but EventLog is null if I don't
    
         this.EventLog.Write(some test event stuff);
      }
    }
    

    This asks the container for the TestHarness instance, and it will import any dependencies as it instantiates it for you.

    As far as keeping a reference to the container around, using singleton, static, servicelocator or whatever method of making it available, that is a similar principle to most other IoC frameworks when you need to ask them to resolve a type for you.