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?
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);