Castle Windsor ApiController Factory implementation for ASP.NET Web API

前端 未结 6 1907
傲寒
傲寒 2020-12-16 03:07

I know it\'s possible to use DependencyResolver and register Castle Windsor with MVC but due to the issues described in https://stackoverflow.com/a/4889222/139392 we have st

相关标签:
6条回答
  • 2020-12-16 03:47

    I have managed to resolve from my windsor container by using GlobalConfiguration.Configuration.ServiceResolver.SetResolver as shown here.

    Example Windsor Code

    private void BootStrapWindsorContainer()
    {
         _container = new WindsorContainer()
              .Install(FromAssembly.This());
    
         var controllerFactory = new WindsorControllerFactory(_container.Kernel);
    
         ControllerBuilder.Current.SetControllerFactory(controllerFactory);
         ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(_container));
         GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
             t =>
                 {
                     try
                    {
                        return _container.Resolve(t);
                    }
                    catch
                    {
                        return null;
                    }
                },
                t =>
                {
                    try
                    {
                        return _container.ResolveAll(t).OfType<object>();
                    }
                    catch
                    {
                 return new List<object>();
             }
        });
    }
    

    Returning null when you cannot resolve types works and it seems like MVC will new up the dependencies.

    Edit: Make sure you have an installer for IHttpController

    0 讨论(0)
  • 2020-12-16 03:53

    I wrote a post about how to do this and hook it up to RavenDB which you might find useful here

    0 讨论(0)
  • 2020-12-16 03:54

    I also faced this issue two days ago and this post helped me. And don't forget to add WebAPI controllers in your Windsor bootstrap.

    container.Register(AllTypes.FromThisAssembly().BasedOn<IHttpController>().LifestyleTransient());
    

    UPDATE for ASP.NET MVC 4 RC: This useful post tells you how to use Windsor with WebAPI, it works like a charm.

    0 讨论(0)
  • 2020-12-16 04:02
    public class WindsorHttpControllerFactory : IHttpControllerActivator
        {
            readonly IWindsorContainer _container;
    
            public WindsorHttpControllerFactory(IWindsorContainer container)
            {
                _container = container;
            }
    
            public IHttpController Create(HttpRequestMessage request,
                                          HttpControllerDescriptor controllerDescriptor,
                                          Type controllerType)
            {
                var controller = (IHttpController)_container.Resolve(controllerType);
    
                request.RegisterForDispose(new Release(() => _container.Release(controller)));
                return controller;
            }
    
            class Release : IDisposable
            {
                readonly Action _release;
    
                public Release(Action release)
                {
                    _release = release;
                }
    
                public void Dispose()
                {
                    _release();
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-16 04:05

    Look at this post

    I didn't switch yet to mvc 4 beta including web.api (I'm still using WCF web api Prev6), but what has been pointed out into the thread seems the way to go

    0 讨论(0)
  • 2020-12-16 04:10

    Thanks to Critiano's post and some searching on-line I managed to get it to work here's the code for anyone else having this issue. I've fgot it working with MVC3 and ASP.NET Web Api Beta but I think the same solution should work for MVC4.

    Firstly I created a WindsorHttpControllerFactory as the ApiControllers use a different factory than the MVC ones.

    public class WindsorHttpControllerFactory : IHttpControllerFactory
    {
        private readonly IKernel kernel;
        private readonly HttpConfiguration configuration;
    
        public WindsorHttpControllerFactory(IKernel kernel, HttpConfiguration configuration)
        {
            this.kernel = kernel;
            this.configuration = configuration;
        }
    
        public IHttpController CreateController(HttpControllerContext controllerContext, string controllerName)
        {
            if (controllerName == null)
            {
                throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", controllerContext.Request.RequestUri.AbsolutePath));
            }
    
            var controller = kernel.Resolve<IHttpController>(controllerName);
            controllerContext.Controller = controller;
            controllerContext.ControllerDescriptor = new HttpControllerDescriptor(configuration, controllerName, controller.GetType());
    
            return controllerContext.Controller;
        }
    
        public void ReleaseController(IHttpController controller)
        {
            kernel.ReleaseComponent(controller);
        }
    }
    

    The tricky part was registration it seems to involved registering a whole bunch of other stuff. This is what I ended up with.

    container.Register(Component.For<IHttpControllerFactory>().ImplementedBy<WindsorHttpControllerFactory>().LifeStyle.Singleton);
    container.Register(Component.For<System.Web.Http.Common.ILogger>().ImplementedBy<MyLogger>().LifeStyle.Singleton);
    container.Register(Component.For<IFormatterSelector>().ImplementedBy<FormatterSelector>().LifeStyle.Singleton);
    container.Register(Component.For<IHttpControllerActivator>().ImplementedBy<DefaultHttpControllerActivator>().LifeStyle.Transient);
    container.Register(Component.For<IHttpActionSelector>().ImplementedBy<ApiControllerActionSelector>().LifeStyle.Transient);
    container.Register(Component.For<IActionValueBinder>().ImplementedBy<DefaultActionValueBinder>().LifeStyle.Transient);
    container.Register(Component.For<IHttpActionInvoker>().ImplementedBy<ApiControllerActionInvoker>().LifeStyle.Transient);
    container.Register(Component.For<System.Web.Http.Metadata.ModelMetadataProvider>().ImplementedBy<System.Web.Http.Metadata.Providers.CachedDataAnnotationsModelMetadataProvider>().LifeStyle.Transient);
    container.Register(Component.For<HttpConfiguration>().Instance(configuration));
    
    //Register all api controllers
    container.Register(AllTypes.FromAssembly(assemblyToRegister)
                                .BasedOn<IHttpController>()
                                .Configure(registration => registration.Named(registration.ServiceType.Name.ToLower().Replace("controller", "")).LifeStyle.Transient));
    
    //Register WindsorHttpControllerFactory with Service resolver
    GlobalConfiguration.Configuration.ServiceResolver.SetService(typeof(IHttpControllerFactory), container.Resolve<IHttpControllerFactory>());
    

    I had to create my own implementation of an ILogger you could use a stub version like bellow.

    public class MyLogger : System.Web.Http.Common.ILogger
    {
        public void LogException(string category, TraceLevel level, Exception exception)
        {
            // Do some logging here eg. you could use log4net
        }
    
        public void Log(string category, TraceLevel level, Func<string> messageCallback)
        {
            // Do some logging here eg. you could use log4net
        }
    }
    
    0 讨论(0)
提交回复
热议问题