如果服务依赖于这样的信息(例如,请求URI),则应该可以将该信息注入到服务中。
这是很容易做到使用穷人的DI:只是implement a custom IHttpControllerActivator。
然而,与城堡温莎这突然变得非常困难。以前,I’ve described a very convoluted way解决这个问题,但它取决于PerWebRequest生活方式,并且事实证明,这种生活方式不工作在自托管场景,因为HttpContext.Current是空的。
到目前为止,我已经能够通过将所需的信息作为内联参数传递给自定义IHttpControllerActivator的Resolve方法:
public IHttpController Create( HttpControllerContext controllerContext,Type controllerType) { var baseUri = new Uri( controllerContext .Request .RequestUri .GetLeftPart(UriPartial.Authority)); return (IHttpController)this.container.Resolve( controllerType,new { baseUri = baseUri }); }
然而,默认情况下,只有当立即请求的类型依赖于参数(即如果请求的Controller本身依赖于baseUri)时,这才有效。如果对baseUri的依赖性在依赖关系层次结构中更深入,则它在默认情况下不起作用,因为内联参数不会传播到更深层。
此行为可以使用自定义IDependencyResolver(Castle Windsor IDependencyResolver,而不是ASP.NET Web API IDependencyResolver)更改:
public class InlineDependenciesPropagatingDependencyResolver : DefaultDependencyResolver { protected override CreationContext RebuildContextForParameter( CreationContext current,Type parameterType) { if (parameterType.ContainsGenericParameters) { return current; } return new CreationContext(parameterType,current,true); } }
请注意,true作为propagateInlineDependencies构造函数参数传递,而不是false,这是默认实现。
为了使用InlineDependenciesPropagatingDependencyResolver类连接容器实例,必须以这种方式构造容器实例:
this.container = new WindsorContainer( new DefaultKernel( new InlineDependenciesPropagatingDependencyResolver(),new DefaultProxyFactory()),new DefaultComponentInstaller());
它违反了大多数开发者在将内联依赖传递给Resolve()(它们只能传递一个级别的依赖关系解析)时所做的假设,并且在某些情况下可能导致依赖关系不正确地覆盖一些其他配置的服务。 (例如,如果您有另一个组件许多级别具有相同类型和名称的依赖关系)。它可能是一个很难识别的错误的潜在原因。
这个问题的核心是一个困难的DI,真正表明IoC不是真正可行的(即我们的依赖需要被“推”,不能被“拉”我们的容器)。在我看来,有两个选择:
1)纠正阻止“反转”的问题。即包装HttpControllerContext / HttpContext,增加该包装器在自承载场景中的行为,并且您的组件依赖于该包装器,而不是直接HttpControllerContext / HttpContext。
2)反映你正在使用的环境的缺点(它不完全支持“反演”),并解决这些缺点非常明确的解决方法。在你的场景中,这可能涉及利用一个类型化的工厂(接口)来实例化在IHttpControllerActivator.Create()中需要baseUri的组件。这意味着如果这个组件进一步依赖于依赖关系层次结构,则需要显式构建您的依赖关系层次结构,直到拥有控制器。
我可能会去第二个选项,只是因为当惯例不剪切,我宁愿尽可能明确。
更新
假设我们有一个控制器类型A,它依赖于组件B,而组件B又依赖于baseUri,第二个选项可能看起来像这样:
// Typed factories for components that have dependencies,which cannot be resolved statically IBFactory bFactory; IAFactory aFactory; public IHttpController Create(HttpControllerContext controllerContext,Type controllerType) { if (controllerType == typeof(A)) { // Special handling for controller where one or more dependencies // are only available via controllerContext. var baseUri = new Uri(controllerContext.Request.RequestUri.GetLeftPart(UriPartial.Authority)); B b = this.bFactory.Create(baseUri); return this.aFactory.Create(b); } // Default for all other controllers return (IHttpController)this.container.Resolve(controllerType); }
关键点是,这明确地处理我们的环境的缺点,它绑定受影响的类型特别是与我们提供的依赖重写我们提供的,并确保我们不会意外覆盖任何其他依赖。