Question

I have a base controller class that exposes some logging functionality to it's child classes. This logging dependency is constructor injected. To provide some simplified code this is how it all looks like:

public abstract class LogControllerBase : Controller
{
    private readonly ILogger logger = null;

    protected LogControllerBase(ILogger logger)
    {
        this.logger = logger;
    }
}

My child controllers also have their own dependencies hence they look something like this:

public class SomeController : LogControllerBase
{
    private readonly IService service = null;

    [ImportingConstructor]
    public SomeController(ILogger logger, IService service)
        : base(logger)
    {
        this.service = service;
    }
}

I'm using these constructors to make it easy to do dependency injection in my unit tests but in production all composition is (should be) done by MEF. I'm using my custom controller factory class that uses MEF to instantiate controllers.

To sum it up:

  1. I have a base abstract controller class that has its own dependencies
  2. I have descendant controllers that use importing to get their parameters injected by MEF (that is their dependencies as well as base class' ones)
  3. Unit tests don't use MEF so mocks are injected to constructor parameters

The problem

This all holds water to me, but MEF thinks differently. When I compile and run this code I get this exception:

GetExportedValue cannot be called before prerequisite import 'SomeController..ctor (Parameter="logger", ContractName="ILogger")' has been set.

All interface types that are used as constructor parameters have attribute InheritedExport set on them and also have concrete implementations, so it should work as expected.

Working alternative which isn't the same

When I try an alternative approach by doing imports directly on those private fields everything seems to work just fine.

public abstract class LogControllerBase : ControllerBase
{
    [Import]
    private ILogger logger = null;

    protected LogControllerBase() { }
}

public class SomeController : LogControllerBase
{
    [Import]
    private IService service = null;

    public SomeController() { }
}

So this works but it's not the same... I could add constructors here for dependency injection, but then I'd have two sets of constructors and when doing unit tests one may use the parameterless constructor which would of course be wrong because no dependencies would get set. Not real ones nor mocks.

Question

How can I convince MEF to create my controllers by injecting concrete implementations in constructor by setting dependency injection constructor as ImportingConstructor?

Was it helpful?

Solution

This might be related to threading, as MEF containers are not constructed as thread-safe by default. In your constructor for your container, try enabling thread-safety:

var container = new CompositionContainer(catalog, true);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top