解决HttpControllerContext与温莎城堡(Resolving HttpControl

2019-06-25 02:00发布

在ASP.NET的Web API ,HttpControllerContext实例提供了很多关于当前环境下,包括当前请求的URI信息。

如果服务依赖于这样的信息(例如,请求URI),它应该是可以注入该信息到服务。

这是很容易使用穷人的DI做:只是实现自定义IHttpControllerActivator 。

然而,与温莎城堡这个突然变得非常困难。 此前, 我已经描述了一个非常曲折的方式来解决这个问题,但它系于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 });
}

然而,在默认情况下,这仅在立即请求的类型依赖于参数(即如果请求控制器本身依赖于工作的baseUri )。 如果依赖baseUri的依赖层次埋设较深,但默认情况下不工作,因为内嵌参数不会传播到更深的层次。

这种行为可以用自定义的IDependencyResolver(一温莎城堡的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());

我不知道这是否是对这个问题的最佳解决方案,或者,如果有一个更好/更简单的方法?

Answer 1:

在我看来,你的InlineDependenciesPropagatingDependencyResolver实际上掩盖的东西到你的应用程序的架构相当关键:一个或多个您的组件有不能可靠静态解析,从容器中,或从动态语境依赖性。

它违反了假设,通过内嵌的依赖时解决()(,他们只得到传承下来的依赖分辨率的水平),并在某些情况下可能会导致依赖性错误地覆盖一些其他配置的服务,大多数开发人员做出。 (例如,如果你有另外的成分多层次的下降,这有相同类型和名称的依赖)。 这可能是错误,这将是很难识别的潜在原因。

在这个心脏的问题是一个很难的DI和真正表明国际奥委会是不是真的可行的(即我们的依赖(IES)需要被“推”和由容器不能被“拉”对我们来说)。 在我看来,有两种选择:

1)矫正,其防止“反转”的问题。 即包裹HttpControllerContext / HttpContext的,这增加包装表现为自托管方案所需,让您的组件依赖于包装,而不是直接HttpControllerContext / HttpContext的。

2)反映您正在使用(它不完全支持“反转”环境的缺点),并解决办法来处理这些缺点很明显。 在你的情况下,这将可能涉及利用类型化的工厂(接口)来实例化需要的组件baseUriIHttpControllerActivator.Create() 这意味着,如果该部件进一步下跌的依赖层次结构,则需要明确建立你的依赖层次结构,直到你有你的控制器。

我可能会去的第二个选项,只是因为时约定不砍它,我喜欢尽可能明确。

更新假设我们有一个控制器类型A ,这依赖于组件B ,而这又依赖于基本URI,第二个选项可能看起来是这样的:

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

其要点是,这明确地与我们的环境的缺点交易,它依赖覆盖我们提供势在必行特异性结合受影响的类型,并确保我们不会意外重写任何其他依赖。



Answer 2:

只是为了完整起见,回答我的克日什托夫·Koźmic(温莎城堡的当前的维护者)在Twitter上有表示,在问题中列出的方法,事实上,实现这一特定目标的正确方法。

(不过,我不能链接到鸣叫,因为基耶斯洛夫的Twitter账号已被保护(微博都没有公开可见。))



文章来源: Resolving HttpControllerContext with Castle Windsor