使用Castle Windsor解决HttpRequestMessage [英] Resolving HttpRequestMessage with Castle Windsor

查看:93
本文介绍了使用Castle Windsor解决HttpRequestMessage的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试从现有帖子中获取建议,以使 HttpRequestMessage 作为Web API中服务的构造函数依赖项可用。



ASP Web API-IoC-解决HttpRequestMessage



使用温莎城堡解决HttpControllerContext

如果所有依赖项只有一个构造函数,此建议也可以正常工作 。但是,当一个依赖项具有多个构造函数时,依赖关系解析将失败。



任何想法如何将其扩展为可与多个构造函数一起使用?



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



现有方法总结如下:



首先在解析 HttpRequestMessage 作为附加的命名参数添加> IHttpControllerActivator :

 公共IHttpController创建(
HttpRequestMessage请求,
HttpControllerDescriptor controllerDescriptor,
类型controllerType)
{
var controller =(IHttpController)container.Resolve(
controllerType,
new {request});

然后在 CreationContext 中传播此参数:

 公共类InlineDependenciesPropagatingDependencyResolver:
DefaultDependencyResolver
{
受保护的覆盖CreationContext RebuildContextForParameter(
CreationContext当前,
类型parameterType)
{
if(parameterType.ContainsGenericParameters)
{
返回当前值;
}

返回new CreationContext(parameterType,current,true);
}
}

当所有依赖项只有一个构造函数时,此方法很好。



在我的情况下,我有一个依赖关系层次结构:




  • 控制器取决于 IServiceA

  • ServiceA 取决于 IServiceB

  • ServiceB 取决于 IServiceC

  • ServiceC 取决于 HttpRequestMessage



其中 ServiceC 看起来像这样:

 公共类ServiceC:IServiceC 
{
私有只读HttpRequestMessage请求;

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

并且 ServiceB 有两个构造函数:

 公共类ServiceB:IServiceB 
{
public ServiceB(string paramForTests)
{
//做东西
}

公共ServiceB(IServiceC serviceC)
{
//做东西
}

但随后温莎无法解析 ServiceC



问题似乎出在SelectEligibleConstructor 逻辑中Windsor / blob / master / src / Castle.Windsor / MicroKernel / ComponentActivator / DefaultComponentActivator.cs rel = nofollow noreferrer title = DefaultComponentActivator> DefaultComponentActivator 。它调用CanResolve 方法/DefaultDependencyResolver.cs rel = nofollow noreferrer> DefaultDependencyResolver 最终出现在以下位置:

 受保护的虚拟布尔CanResolveFromKernel(CreationContext上下文,ComponentModel模型,DependencyModel依赖项)
{
if(dependency.ReferencedComponentName!= null)
{
//用户要覆盖
返回HasComponentInValidState (dependency.ReferencedComponentName,依赖项,上下文);
}
if(dependency.Parameter!= null)
{
返回true;
}
如果(typeof(IKernel).IsAssignableFrom(dependency.TargetItemType))
{
返回true;
}

if(dependency.TargetItemType.IsPrimitiveType())
{
返回false;
}

返回HasAnyComponentInValidState(dependency.TargetItemType,依赖项,上下文);
}

然后 HasAnyComponentInValidState 只是查看 ServiceC 是否已经解决,实际上并没有检查是否可以解决



如果只有一个构造函数,那么代码将调用 Resolve 方法,该方法可以正确地递归地解决依赖关系,并且ServiceC可以使用。

我不想将服务限制为只有一个构造函数(或将 [DoNotSelect] 属性仅用于



有什么想法可以像我一样注入参数,并且仍然可以与多个构造函数一起使用?

解决方案

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



只需使用工厂方法来启动依赖于 HttpRequestMessage 的服务,您可以在其中手动执行从控制器激活器传递的上下文中选择所需的请求参数:

 公共类Installer:IWindsorInstaller 
{
public void Install(IWindsorContainer container,IConfigurationStore存储)
{
container.Register(
组件
。对于< IServiceC>()
.UsingFactoryMethod((内核,上下文)=> {
var request =(HttpRequestMessage)context.AdditionalArguments [ request];
var serviceC = new ServiceC(request);
退货服务C;
})
.LifestyleTransient()
);
}
}


I've tried following advice from existing posts to make HttpRequestMessage available as a constructor dependency for services in Web API:

ASP Web Api - IoC - Resolve HttpRequestMessage

Resolving HttpControllerContext with Castle Windsor

This advice works fine if all the dependencies only have one constructor. But when a dependency has multiple constructors, dependency resolution fails.

Any ideas how to extend the idea to work with multiple constructors?

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

The existing approach is summarised as follows:

First you add the HttpRequestMessage as an additional named argument when resolving the controller in your IHttpControllerActivator:

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

Then you propagate this argument in the CreationContext:

public class InlineDependenciesPropagatingDependencyResolver :
    DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(
        CreationContext current,
        Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

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

This works fine when all the dependencies only have one constructor.

In my case, I have a hierarchy of dependencies:

  • The controller depends on IServiceA
  • ServiceA depends on IServiceB
  • ServiceB depends on IServiceC
  • ServiceC depends on HttpRequestMessage

Where ServiceC looks like this:

public class ServiceC: IServiceC
{
    private readonly HttpRequestMessage request;

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

And ServiceB has two constructors:

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

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

But then Windsor fails to resolve ServiceC.

The problem seems to be in the SelectEligibleConstructor logic of DefaultComponentActivator. It calls into the CanResolve method in DefaultDependencyResolver which eventually ends up at:

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

And then HasAnyComponentInValidState just looks at whether ServiceC has already been resolved, it doesn't actually check whether it can be resolved.

If there is only one constructor, then the code calls the Resolve methods, which correctly recursively resolve the dependencies, and ServiceC is available ok.

I don't want to limit my services to only having one constructor (or use the [DoNotSelect] attribute to only leave one for Castle to look at).

Any ideas how to inject arguments as I have done, and still have it work with multiple constructors?

解决方案

I found an alternative way of resolving the HttpRequestMessage parameter, which now works.

Just use a factory method to spin up the service with the dependency on HttpRequestMessage, where you manually extract the required "request" argument from the context which has been passed down from the controller activator:

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

这篇关于使用Castle Windsor解决HttpRequestMessage的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆