2

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.

JMarsch
  • 21,484
  • 15
  • 77
  • 125
  • Yeah, you need to compose parts if the object isn't instantiated by the container itself. You can export TestHarness and ask the container to give you an instance of it (instead of new-ing it up) and it will then inject the IEventLog instance for you – Matt Oct 27 '11 at 00:55

1 Answers1

2

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.

Matt
  • 2,984
  • 1
  • 24
  • 31