Search code examples
asp.netasp.net-mvc-4unity-containermef

MEF + Unity Integration error in MVC4 application


I am creating a Microsft MVC4 app with MEF and Unity integration to handle a plugin architecture (other MVC4 projects) and their dependancies. However I'm receiving an error when attemping to select an MEF imported controller that contains a Unity dependancy. Here's the error:

1) Cannot create an instance of type 'X' because a constructor could not be selected for construction. Ensure that the type either has a default constructor, or a single constructor marked with the 'System.ComponentModel.Composition.ImportingConstructorAttribute'.

The main MVC4 app MEF + Unity integration code:

// Create MEF catalog from plugin directory.
var aggregateCatalog = new AggregateCatalog(new DirectoryCatalog(HostingEnvironment.MapPath("~/bin"), "*.dll"));

// Setup Unity container based on catalog.
var unityContainer = new UnityContainer();
unityContainer.RegisterCatalog(aggregateCatalog);  

// Register dependancy.
unityContainer.RegisterType<ISystemService, SystemService>();

// Compose the MEF container.
CompositionContainer container = new CompositionContainer(aggregateCatalog, true);
container.ComposeParts(aggregateCatalog.Catalogs);

// Register the controller factory that will handle requests.
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
var dependencyResolver = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver;
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new MefDependencyResolver(container);

The CreateController method to select proper controller from MEF

// Get all the exported controllers.
var exports = _container.GetExports<IController, INameMetadata>();

// Select controller based off meta data.
var controller = exports.Where(e => e.Metadata.Name.ToLower().Equals(controllerName.ToLower())
            && e.Metadata.Module.ToLower().Equals(module.ToLower()))
    .Select(e => e.Value).FirstOrDefault();            

The Set up of the Controller I'm trying to import. It's a subclass of a abstract base class.

Base class.

public abstract class WidgetController : Controller
{

    [Dependency]
    public ISystemService _systemService { get; set; }

    [ImportingConstructor]
    public WidgetController(ISystemService systemService)
    {
        _systemService = systemService;
    }
}

Child class.

[InheritedExport(typeof(IController))]
[ExportMetadata("Module", "TheComponent")]
[ExportMetadata("Name", "TheName")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class WidgetXController : WidgetController 
{    
    public WidgetXController (ISystemService systemService)
        : base(systemService)
    {      
    }
}

With this structure the WidgetXController is available as an export in the CreateController method. However when attempting to select it in the LINQ query the I get the above error message. I've attempted adding the [ImportingConstructor] attribute to the child class but that results in the controller from getting into the export list. I'm thinking something isn't quite set up correctly in the MEF + Unity integration...

Does anyone have any insight on how the MEF + Unity integration functions that could help resolve the error I see?


Solution

  • I found a solution. Instead of creating a new composition container, I needed to obtain it from Unity.

    So the orginal code of:

    // Compose the MEF container.
    CompositionContainer container = new CompositionContainer(aggregateCatalog, true);
    container.ComposeParts(aggregateCatalog.Catalogs);
    
    // Register the controller factory that will handle requests.
    ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
    var dependencyResolver = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver;
    System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new MefDependencyResolver(container);
    

    Becomes:

    // Obtain the MEF container from Unity
    var mefContainer = unityContainer.Resolve<CompositionContainer>();
    mefContainer.ComposeParts(this);
    
    // Register the controller factory that will handle requests.
    ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(mefContainer));
    var dependencyResolver = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver;
    System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new MefDependencyResolver(mefContainer);