I found out why the placeholder "Home Controller" was getting called here
I commented out the reference to HomeController in RouteConfig.cs and added a Controller that I want it to call instead:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//If get 404 error re: favicon, see http://docs.castleproject.org/(X(1)S(vv4c5o450lhlzb45p5wzrq45))/Windsor.Windsor-tutorial-part-two-plugging-Windsor-in.ashx or just uncomment the line below:
//routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });
//routes.MapRoute(
// name: "Default",
// url: "{controller}/{action}/{id}",
// defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
//);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "DepartmentsController", action = "GetCountOfDepartmentRecords", id = UrlParameter.Optional }
);
}
}
...but that gives me a null here in WindsorControllerFactory:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)_kernel.Resolve(controllerType);
}
...even though GetCountOfDepartmentRecords() does exist in DepartmentsController:
public int GetCountOfDepartmentRecords()
{
return _deptsRepository.Get();
}
So what am I missing as far as setup or configuration or ... ???
Note: I also tried the RouteConfig entry sans the "Controller" verbiage:
defaults: new { controller = "Departments", action = "GetCountOfDepartmentRecords", id = UrlParameter.Optional }
...but it makes no difference.
UPDATE
This is what I have in WindsorDependencyResolver:
public class ApiControllersInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<ApiController>()
.LifestylePerWebRequest());
}
}
...am I doing something wrong?
UPDATE 2
Perhaps I have too many classes that implement IWindsorInstaller in my project?
&& ||, relatedly, I'm calling container.Register() too many times/in too many places?
I've got calls to container.Register() in three spots:
0) WindsorDependencyResolver.cs:
public class ApiControllersInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<ApiController>()
.LifestylePerWebRequest());
}
}
1) SomethingProviderInstaller.cs:
public class SomethingProviderInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(ISomethingProvider))
.WithServiceAllInterfaces());
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IMyFirstFactory>().AsFactory());
}
}
2) RepositoriesInstaller
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Types.FromThisAssembly()
.Where(type => type.Name.EndsWith("Repository"))
.WithService.DefaultInterfaces()
.Configure(c => c.LifestylePerWebRequest()));
}
}
Is this "too much of a good thing"?
UPDATE 3
My take on it is that this:
public class ApiControllersInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<ApiController>()
.LifestylePerWebRequest());
}
}
...should register everything that implements the ApiController interface; so, DepartmentsController should be installed.
public class DepartmentsController : ApiController
(as should all of the other Controllers, as they all implement the ApiController interface).
While this one:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(ISomethingProvider))
.WithServiceAllInterfaces());
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IMyFirstFactory>().AsFactory());
}
...would only register the single class that implements ISomethingProvider.
And finally this one:
container.Register( Types.FromThisAssembly() .Where(type => type.Name.EndsWith("Controllers")) .WithService.DefaultInterfaces() .Configure(c => c.LifestylePerWebRequest()));
...would register everything named Controller.
Note: I replaced:
.Where(type => type.Name.EndsWith("Repository"))
...with what it is above (Repository).
I put breakpoints in them all, and they were all hit following were hit, in reverse order from how they are shown above. So I don't know if only one call to container.Register() is allowed, or why I'm getting a null Controller...
UPDATE 4
Based on the example here by Summit Dishpanhands, I commented out the code I had in RepositoriesInstaller and replaced it with this:
container.Register(
Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository>()
);
...still no joy in Mudville, though...
UPDATE 5
Using Summit's code here [Dependency Injection in WebAPI with Castle Windsor, I changed the code in Global.asax.cs from:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
BootstrapContainer();
}
private static void BootstrapContainer()
{
_container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(_container));
}
...to this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureWindsor(GlobalConfiguration.Configuration);
}
public static void ConfigureWindsor(HttpConfiguration configuration)
{
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
}
...and I seem to have gotten further. Now, instead of a runtime exception, I get a browsetime exception, namely HTTP Error 403.14 - Forbidden:
Do I really need to mess with ISS, or is there something else that would solve this conundrum?
UPDATE 6
In response to Kiran Challa:
When I change it to MapHttpRoute like so:
routes.MapHttpRoute(
name: "Departments",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Departments", action = "GetCountOfDepartmentRecords", id = UrlParameter.Optional }
);
...I get the compile-time error "'System.Web.Routing.RouteCollection' does not contain a definition for 'MapHttpRoute' and no extension method 'MapHttpRoute' accepting a first argument of type 'System.Web.Routing.RouteCollection' could be found..."
UPDATE 7
Thanks for your help, Kiran.
I do have a WebApiConfig.cs, and it actually already has all this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApiWithParameters",
routeTemplate: "api/{controller}/{ID}/{CountToFetch}"
//defaults: new { ID = RouteParameter.Optional, CountToFetch = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApiWith3Parameters",
routeTemplate: "api/{controller}/{ID}/{packSize}/{CountToFetch}"
//defaults: new { ID = RouteParameter.Optional, packSize = RouteParameter.Optional, CountToFetch = RouteParameter.Optional }
);
}
}
...so I guess I should just comment out the stuff in RouteConfig.cs; it seems I've got too many files; it's as if my closet is chockfull of flammables.
UPDATE 8
For Adam Connelly:
"I'm assuming you mean that controllerType is null."
Yes.
"Where you currently have something like the following:
var controllerFactory = new WindsorControllerFactory(container.Kernel); ControllerBuilder.Current.SetControllerFactory(controllerFactory); You'll need to do something like this as well (or instead if your project only uses WebApi):
GlobalConfiguration.Configuration.Services.Replace( typeof (IHttpControllerActivator), new WindsorControllerActivator(container));"
Actually, I did have both of them:
private static void BootstrapContainer()
{
_container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(_container));
}
...but am currently not calling BootstrapContainer(), so neither is called. What replaced that is:
private static IWindsorContainer _container;
. . .
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureWindsor(GlobalConfiguration.Configuration);
}
public static void ConfigureWindsor(HttpConfiguration configuration)
{
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
}
UPDATE 9
Serdar, this seems very helpful, especially the ServiceInstaller : IWindsorInstaller method.
Now in that, I see that IRepository has a different lifestyle (LifestyleTransient) than the rest (LifestylePerWebRequest).
Is IRepository something I should have a reference to (I don't, it's unresolvable), or is it a custom interface of yours, such as all the rest appear to be (IUserService, IAppService, etc.)?