I really dont understand what I’m doing wrong here – for some reason the IControllerFactory i register is not being used, and I end up with a System.ArgumentException:
Type ‘Company.WebApi.Controllers.AController’ does not have a default
constructorat System.Linq.Expressions.Expression.New(Type type) at
System.Web.Http.Internal.TypeActivator.Create[TBase](Type
instanceType) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage
request, Type controllerType, Func`1& activator) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType)
In Global.asax.cs
protected void Application_Start()
{
//DI-setup
var container = new WindsorContainer().Install(new WebWindsorInstaller());
//set custom controller factory
var controllerFactory = container.Resolve<IControllerFactory>();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
//register cors
container.Resolve<ICorsConfig>().RegisterCors(GlobalConfiguration.Configuration);
//routes
RegisterRoutes(RouteTable.Routes);
}
My IControllerFactory is based on this article http://keyvan.io/custom-controller-factory-in-asp-net-mvc and is implemented as following
public class ControllerFactory : IControllerFactory
{
private readonly IWindsorContainer _container;
public ControllerFactory(IWindsorContainer container)
{
if(container == null)
throw new ArgumentNullException("container");
_container = container;
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
var componentName = GetComponentNameFromControllerName(controllerName);
return _container.Resolve<IController>(componentName);
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
throw new NotImplementedException();
}
public void ReleaseController(IController controller)
{
_container.Release(controller);
}
static string GetComponentNameFromControllerName(string controllerName)
{
var controllerNamespace = typeof (CrudController<>).Namespace;
return string.Format("{0}.{1}Controller", controllerNamespace, controllerName);
}
}
I’ve been staring at this for hours, and really can’t see why this isn’t working. When debugging the ControllerFactory is never hit in any method except the constructor. Anyone see whats wrong or missing here?
(Setting ASP.NET MVC ControllerFactory has no effect does not answer my question)
Edit 1 – Controller
public class AController : CrudController<AModel>
{
public AController(IAHandler aHandler) : base(aHandler)
{
...
}
[HttpGet]
public HttpResponseMessage GetByUser(string aId)
{
...
}
}
public abstract class CrudController<T> : ApiController
where T : IModel, new()
{
protected CrudController(ICrudHandler<T> handler)
{
...
}
[HttpGet]
public HttpResponseMessage Get(string id)
{
...
}
[HttpPost]
public HttpResponseMessage Post(T input)
{
...
}
[HttpPut]
public HttpResponseMessage Put(T input)
{
...
}
}
Edit 2 – Installer
public sealed class WebWindsorInstaller : IWindsorInstaller
{
private bool _installComplete;
public void Install(IWindsorContainer container, IConfigurationStore store)
{
if(_installComplete)
return;
//register self for reuse
container.Register(Component.For<IWindsorInstaller>().Instance(this));
//controller factory
container.Register(
Component.For<IControllerFactory>().ImplementedBy<ControllerFactory>().LifeStyle.Singleton);
//Handlers
container.Register(
Classes.FromAssemblyContaining<AHandler>().InSameNamespaceAs<AHandler>().WithService.
DefaultInterfaces());
//Models
container.Register(
Classes.FromAssemblyContaining<AModel>().InSameNamespaceAs<AModel>().WithService.Self());
//DI
container.Register(Component.For<IWindsorContainer>().Instance(container));
container.Register(Component.For<IDependencyResolver>().ImplementedBy<WindsorDependencyResolver>());
//Controllers
container.Register(Classes
.FromAssemblyContaining<AController>()
.BasedOn<IHttpController>()
.LifestyleScoped());
//repositories
container.Register(
Classes.FromAssemblyContaining<ARepository>().InSameNamespaceAs<ARepository>().WithService.
DefaultInterfaces());
//cors
container.Register(Component.For<ICorsConfig>().ImplementedBy<CorsConfig>());
_installComplete = true;
}
}
From the error messages in your question it looks like you actually want to register WebAPI controllers. It’s important to note that these are not the same as MVC controllers, and do not use the same controller factory (which you are trying to use).
See Mark Seeman’s post Dependency Injection in ASP.NET Web API with Castle Windsor for details of how to do this for Web API controllers.