使用Castle Windsor解决HttpRequestMessage [英] Resolving HttpRequestMessage with Castle Windsor
问题描述
我尝试从现有帖子中获取建议,以使 HttpRequestMessage
作为Web API中服务的构造函数依赖项可用。
ASP Web API-IoC-解决HttpRequestMessage
如果所有依赖项只有一个构造函数,此建议也可以正常工作 。但是,当一个依赖项具有多个构造函数时,依赖关系解析将失败。
任何想法如何将其扩展为可与多个构造函数一起使用?
=====================
现有方法总结如下:
首先在解析 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
。
问题似乎出在
受保护的虚拟布尔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 onIServiceB
ServiceB
depends onIServiceC
ServiceC
depends onHttpRequestMessage
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屋!