I have got quite a complex project on the go at the moment, and as part of this I have a MEF layer which purely handles loading of plugins and then the newly loaded plugins expose their routes which are registered with asp.mvc and their controllers which are added to Ninject's bindings.
The problem however comes in when the dynamically added routes are hit (and they are hit, I have checked with route debugger) even with correctly added Namespaces for the plugin within the route. When I say I have added the namespaces I mean like below:
var namespaces = new [] { "MyPlugin.Controllers" };
routeCollection.MapRoute(
PluginRoute, "plugin/{action}",
new { controller = "Plugin", action = "Default" },
namespaces);
Just to give a little more context to this situation, I am inheriting from NinjectHttpApplication and not doing anything else, no custom controller factories, no custom dependency resolvers, just what Ninject gives me. Then I take the currently active Kernel, give it to the plugins and they register themselves.
Now the routes that are hit do not work, I just get a 404 for any external routes, even though they are hit and the controller is (yes tripple checked) registered with the Ninject Kernel. So I am thinking that although Ninject has the type registered, Mvc's DefaultControllerFactory cannot find the type when calling through to:
GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces)
One thing that baffles me at the moment though, is that it is not finding it even with the correct namespace... HOWEVER just to prove my hypothisis, if开发者_高级运维 I add the plugin as a reference within the asp mvc project and run it (without changing any code, just the plugin assembly is a reference within the project, so it will end up within the bin directory) it will work. Hits the route and I get the desired output...
So at this point I am wondering if although MEF is hosting the external DLLs, it is not sharing it in some way with the current AppDomain or something... which seems odd...
This is a blocker for me at the moment, so any advice would be great!
It seems the DefaultControllerFactory
does not know which type is responsible for handling the requests. You have either to find out why the DefaultControllerFactory does not know about these controllers or provide your own implementation that is able to handle these cases. The problem is definitely more MEF related than Ninject related.
public class MyControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
Type controllerType = this.GetControllerType(requestContext, controllerName) ?? this.GetPluginControllerType(requestContext, controllerName)
return this.GetControllerInstance(requestContext, controllerType);
}
private Type GetPluginControllerType(RequestContext requestContext, string controllerName)
{
// put your own implementation here
}
}
Another solution is to use Ninject'ss assembly loading mechanism instead of MEF.
Sounds mef related, what happens when you try to instantiate the classes from the kernel directly yourself? If it works you could just create your own controller factory:
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel _kernel;
public NinjectControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
return (IController)_kernel.Get(controllerType);
}
}
Although these days with mvc 3 I don't bother with the controllerfactory and go in at dependency resolver level.
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel _kernel;
public NinjectDependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType);
}
}
n.b. both need to be registed in application_start respectively (although you'd only use one)
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(_kernel));
DependencyResolver.SetResolver(new NinjectDependencyResolver(_kernel));
精彩评论