ASP.NET Web API 的 Castle Windsor ApiController Factory 实现

Posted

技术标签:

【中文标题】ASP.NET Web API 的 Castle Windsor ApiController Factory 实现【英文标题】:Castle Windsor ApiController Factory implementation for ASP.NET Web API 【发布时间】:2012-03-22 09:49:04 【问题描述】:

我知道可以使用 DependencyResolver 并将 Castle Windsor 注册到 MVC,但由于 https://***.com/a/4889222/139392 中描述的问题,我们在 MVC 项目中坚持使用 WindsorControllerFactory 实现方法。

但是看起来 ApiControllers 正在使用某种其他类型的工厂,因为 Castle Windsor 无法注入依赖项。

有没有人想出如何在不使用 DependencyResolver 的情况下将 Castle Windsor 与 ASP.NET Web API 和 MVC 一起使用?

【问题讨论】:

见***.com/a/19613137/114029 【参考方案1】:

感谢 Critiano 的帖子和一些在线搜索,我设法让它工作这里是其他人遇到此问题的代码。我已经将它与 MVC3 和 ASP.NET Web Api Beta 一起使用,但我认为相同的解决方案应该适用于 MVC4。

首先,我创建了一个 WindsorHttpControllerFactory,因为 ApiControllers 使用与 MVC 不同的工厂。

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);
    

棘手的部分是注册它似乎涉及注册一大堆其他东西。这就是我最终的结果。

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>());

我必须创建自己的 ILogger 实现,您可以使用像下面这样的存根版本。

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
    

【讨论】:

【参考方案2】:

两天前我也遇到了这个问题,这个post 帮助了我。并且不要忘记在 Windsor 引导程序中添加 WebAPI 控制器。

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

ASP.NET MVC 4 RC 更新:useful post 告诉您如何将 Windsor 与 WebAPI 一起使用,它就像一个魅力。

【讨论】:

【参考方案3】:

看看这个post

我还没有切换到包含 web.api 的 mvc 4 beta(我仍在使用 WCF web api Prev6),但是线程中指出的内容似乎是可行的方法

【讨论】:

【参考方案4】:

我写了一篇关于如何做到这一点并将其连接到 RavenDB 的帖子,您可能会发现它很有用 here

【讨论】:

【参考方案5】:

我已设法通过使用 GlobalConfiguration.Configuration.ServiceResolver.SetResolver 从我的 Windsor 容器中解决问题,如 here 所示。

温莎代码示例

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>();
         
    );

当您无法解析类型时返回 null 有效,并且似乎 MVC 将新建依赖项。

编辑: 确保你有 IHttpController 的安装程序

【讨论】:

此方法存在潜在的内存泄漏问题,正如我在此处的更多信息中所述。 ***.com/questions/4140860/… 也建议在他们的文档中使用控制器工厂stw.castleproject.org/…【参考方案6】:
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();
            
        
    

【讨论】:

以上是关于ASP.NET Web API 的 Castle Windsor ApiController Factory 实现的主要内容,如果未能解决你的问题,请参考以下文章

使用 Castle 单例是不是会导致 ASP.NET Web 应用程序中的争用

csharp 用于ASP.NET MVC的Castle Windsor IoC容器设置

Web API1.1 ASP.NET Web API入门

Asp.Net Web API 2第五课——Web API路由

ASP.NET Web API教程1.1 第一个ASP.NET Web API

Asp.Net Web API 2第三课——.NET客户端调用Web API