Reputation: 1574
After loading the web application, Castle Windsor cannot find the controllers. The controller for path '' was not found or does not implement IController. When I look into the kernel (in the CustomControllerFactory) I see that all the controllers are correctly registered.
The main MVC application loads 3 other DLL's. When we directly reference the DLL's in Visual studio and load the Plugin types it is working. But when dynamically loading it, it says fails. When I request a URL the context being passed into the GetControllerInstance is correct but the Type parameter is null.
I am loading an assembly using Assembload.LoadFrom.Then I retrieve the Types foreach module, which is a subclass of Plugin. Which results in the 3 types I have.
Assembly assembly = Assembly.LoadFrom(module);
Type pluginType = assembly.GetTypes()
.Single(x => x.IsSubclassOf(typeof(Plugin)));
Then I create an instance of the Plugin, which I use to register the routes.
(IPlugin)Activator.CreateInstance(type))
RegisterRoutes:
public static void RegisterRoutes(RouteCollection routes, IEnumerable<IPlugin> plugins)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var pluginRouteDefaults = new {action = "Index", id = UrlParameter.Optional};
foreach (var plugin in plugins)
{
var context = new AreaRegistrationContext(plugin.Area, routes);
context.MapRoute(plugin.Area, $"{plugin.Area}/{{controller}}/{{action}}/{{id}}", pluginRouteDefaults, plugin.GetControllerNamespaces().ToArray());
}
routes.MapRoute(
name: "Default",
url: "{area}/{controller}/{action}/{id}",
defaults: new { area = "Framework", controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "Web.Framework.Controllers" }
);
}
CustomControllerFactory:
public class CustomControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
public VlcControllerFactory(IKernel kernel)
{
this._kernel = kernel;
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType == null)
{
return base.GetControllerInstance(context, controllerType);
}
try
{
return (IController)_kernel.Resolve(controllerType);
}
catch
{
return base.GetControllerInstance(context, controllerType);
}
}
}
Registering the controllers. After doing this I can see that in the Modules window in Visual Studio the DLL's are loaded. Also the AppDomain.CurrentDomain.GetAssemblies() says that the DLL's are loaded.
container.Register(
Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
MvcApplication class where I locate the Dll's.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var directories = Directory.GetDirectories("C:\Projects\main\modules").Where(x => !x.EndsWith("Framework"));
string subPath = GetSubPath();
List<Type> pluginTypes = GetPluginTypes(directories, subPath);
var plugins = GetIPlugins(pluginTypes);
Launcher.CreateWindsorContainer(plugins.ToArray());
}
private static List<IPlugin> GetIPlugins(List<Type> pluginTypes)
{
List<IPlugin> plugins = new List<IPlugin>{new MvcInstaller()};
pluginTypes.ForEach(type => plugins.Add((IPlugin) Activator.CreateInstance(type)));
return plugins;
}
private static List<Type> GetPluginTypes(IEnumerable<string> directories, string subPath)
{
List<Type> pluginTypes = new List<Type>();
foreach (string directory in directories)
{
string module = Directory.GetFiles(directory + subPath).SingleOrDefault(x => x.EndsWith("Plugin.dll"));
if (!string.IsNullOrEmpty(module))
{
Assembly assembly = Assembly.LoadFrom(module);
Type pluginType = assembly.GetTypes()
.Single(x => x.IsSubclassOf(typeof(Plugin)));
pluginTypes.Add(pluginType);
}
}
return pluginTypes;
}
private static string GetSubPath()
{
#if DEBUG
var subPath = @"\bin\Debug\";
#else
subPath = @"\bin\Release\";
#endif
return subPath;
}
}
When I omit this code and directly reference the other Dll's and do the following:
Launcher.CreateWindsorContainer(new PluginA(), new PluginB(), new MVCPlugin());
Then it works perfect, but with my loading of Dlls, the resolving of the controllers is failing. Why can Castle Windsor not find the Types when requesting a Controller?
Upvotes: 3
Views: 880
Reputation: 675
The problem here is not the Windsor resolution of your controllers. The DefaultControllerType.GetControllerType()
method is probably returning null
, because you didn't add the assemblies to the BuildManager
(with BuildManager.AddReferencedAssembly(assembly)
). Keep in mind you can only call this before application start, so you need to use [assembly:PreApplicationStartupMethod(typeof(...SomeType), "PublicStaticVoidMethodOnSomeType")
.
Upvotes: 3