使用Castle Windsor解决HttpRequestMessage问题

时间:2018-06-14 10:48:42

标签: c# asp.net-web-api dependency-injection castle-windsor

我已尝试按照现有帖子的建议,使HttpRequestMessage可用作Web API中服务的构造函数依赖项:

ASP Web Api - IoC - Resolve HttpRequestMessage

Resolving HttpControllerContext with Castle Windsor

如果所有依赖项只有一个构造函数,那么这个建议可以正常运行。但是,当依赖项具有多个构造函数时,依赖项解析将失败。

如何扩展这个想法以使用多个构造函数的任何想法?

=======================

现有方法总结如下:

首先在解析HttpRequestMessage中的控制器时添加IHttpControllerActivator作为额外的命名参数:

public IHttpController Create(
    HttpRequestMessage request,
    HttpControllerDescriptor controllerDescriptor,
    Type controllerType)
{
    var controller = (IHttpController)container.Resolve(
        controllerType,
        new { request });

然后在CreationContext

中传播此参数
public class InlineDependenciesPropagatingDependencyResolver :
    DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(
        CreationContext current,
        Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

        return new CreationContext(parameterType, current, true);
    }
}

当所有依赖项只有一个构造函数时,这可以正常工作。

就我而言,我有一个依赖层次结构:

  • 控制器取决于IServiceA
  • ServiceA取决于IServiceB
  • ServiceB取决于IServiceC
  • ServiceC取决于HttpRequestMessage

ServiceC看起来像这样:

public class ServiceC: IServiceC
{
    private readonly HttpRequestMessage request;

    public ServiceC(HttpRequestMessage request)
    {
        this.request = request;
    }

ServiceB有两个构造函数:

public class ServiceB: IServiceB
{
    public ServiceB(string paramForTests)
    {
        // Do stuff
    }

    public ServiceB(IServiceC serviceC)
    {
        // Do stuff
    }

但是,温莎未能解决ServiceC

问题似乎出现在DefaultComponentActivatorSelectEligibleConstructor逻辑中。它调用了DefaultDependencyResolver中的CanResolve方法,该方法最终结束于:

protected virtual bool CanResolveFromKernel(CreationContext context, ComponentModel model, DependencyModel dependency)
{
    if (dependency.ReferencedComponentName != null)
    {
        // User wants to override
        return HasComponentInValidState(dependency.ReferencedComponentName, dependency, context);
    }
    if (dependency.Parameter != null)
    {
        return true;
    }
    if (typeof(IKernel).IsAssignableFrom(dependency.TargetItemType))
    {
        return true;
    }

    if (dependency.TargetItemType.IsPrimitiveType())
    {
        return false;
    }

    return HasAnyComponentInValidState(dependency.TargetItemType, dependency, context);
}

然后HasAnyComponentInValidState只关注ServiceC是否已经解决,它实际上并未检查是否可以解决。

如果只有一个构造函数,则代码调用Resolve方法,这些方法正确地递归解析依赖项,并且ServiceC可用。

我不想将我的服务限制为只有一个构造函数(或使用[DoNotSelect]属性只留下一个供Castle查看)。

如何按照我的方式注入参数,并且仍然可以使用多个构造函数吗?

1 个答案:

答案 0 :(得分:0)

我找到了解决HttpRequestMessage参数的另一种方法,该方法现在可以使用。

只需使用工厂方法来启动依赖于HttpRequestMessage的服务,您就可以在此方法中从控制器激活器传递的上下文中手动提取所需的“请求”参数:

public class Installer : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component
                .For<IServiceC>()
                .UsingFactoryMethod((kernel, context) => {
                    var request = (HttpRequestMessage)context.AdditionalArguments["request"];
                    var serviceC = new ServiceC(request);
                    return serviceC;
                })
                .LifestyleTransient()
        );
    }
}