我正在尝试使用ASP.NET WebAPI设置温莎城堡。
我还使用了Hyprlinkr软件包(https://github.com/ploeh/Hyprlinkr),因此需要将HttpRequestMessage的实例注入(inject)到 Controller 的依赖项之一中。
我正在关注Mark Seemann-http://blog.ploeh.dk/2012/04/19/WiringHttpControllerContextWithCastleWindsor.aspx的这篇文章,但是我发现尽管API运行了,但是当我对其进行调用时,请求只是挂起了。没有错误讯息。就好像它处于无限循环中一样。它卡在我的Custom ControllerActivator中的Resolve调用上
我以为我的城堡注册有误。如果删除上面文章中提到的内容,则可以成功调用API(尽管无需解决依赖关系)
有任何想法吗?
代码在下面
//Global.asax
public class WebApiApplication : HttpApplication
{
private readonly IWindsorContainer container;
public WebApiApplication()
{
container =
new WindsorContainer(
new DefaultKernel(
new InlineDependenciesPropagatingDependencyResolver(),
new DefaultProxyFactory()),
new DefaultComponentInstaller());
container.Install(new DependencyInstaller());
}
protected void Application_Start()
{
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorCompositionRoot(this.container));
}
// installer
public class DependencyInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<ValuesController>()
.Named("ValuesController")
.LifeStyle.PerWebRequest,
Component.For<IResourceLinker>()
.ImplementedBy<RouteLinker>()
.LifeStyle.PerWebRequest,
Component.For<IResourceModelBuilder>()
.ImplementedBy<ResourceModelBuilder>()
.LifeStyle.PerWebRequest,
Component.For<HttpRequestMessage>()
.Named("HttpRequestMessage")
.LifeStyle.PerWebRequest
);
}
}
//Activator
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller = (IHttpController)this.container.Resolve(controllerType, new { request = request });
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
// DependencyResolver
public class InlineDependenciesPropagatingDependencyResolver : DefaultDependencyResolver
{
protected override CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
{
if (parameterType.ContainsGenericParameters)
{
return current;
}
return new CreationContext(parameterType, current, true);
}
}
编辑***********
附加信息****************
因此,我设置了一个场景,其中 Controller 仅将HttpRequestMessage作为ctor参数,然后发现:
这有效:
//controller
public class ValuesController : ApiController
{
private readonly HttpRequestMessage _httpReq;
public ValuesController(HttpRequestMessage httpReq)
{
_httpReq = httpReq;
}
//IHttpControllerActivator
public IHttpController Create(
HttpRequestMessage httpRequest,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller = (IHttpController)this.container.Resolve(
controllerType, new { httpReq = httpRequest });
return controller;
但是,事实并非如此。
//controller
public class ValuesController : ApiController
{
private readonly HttpRequestMessage _httpReq;
public ValuesController(HttpRequestMessage request)
{
_httpReq = request;
}
//IHttpControllerActivator
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller = (IHttpController)this.container.Resolve(
controllerType, new { request = request });
return controller;
即,当匿名对象具有名为“request”的属性,而 Controller ctor arg称为“request”时。它使 Controller 以某种方式认为它的request属性为null。这是什么导致我看到错误的原因:
读过这个
How can I enrich object composition in StructureMap without invoking setter injection?
它解释了类似的情况。
当然,hyprlinkr为HttpRequestMessage提供了ctor arg,称为“request”,因此我确实需要使用该属性名称指定anon对象。
有任何想法吗?
最佳答案
这是一个对我有用的合成根:
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller = (IHttpController)this.container.Resolve(
controllerType,
new
{
request = request
});
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
这是我创建容器的方法:
this.container =
new WindsorContainer(
new DefaultKernel(
new InlineDependenciesPropagatingDependencyResolver(),
new DefaultProxyFactory()),
new DefaultComponentInstaller())
.Install(new MyWindsorInstaller());
这是InlineDependenciesPropagatingDependencyResolver:
public class InlineDependenciesPropagatingDependencyResolver :
DefaultDependencyResolver
{
protected override CreationContext RebuildContextForParameter(
CreationContext current,
Type parameterType)
{
if (parameterType.ContainsGenericParameters)
{
return current;
}
return new CreationContext(parameterType, current, true);
}
}
最后,这是我注册RouteLinker的方法:
container.Register(Component
.For<RouteLinker, IResourceLinker>()
.LifestyleTransient());
要注意的一件事是ApiController基类具有HttpRequestMessage类型的名为
Request
的公共(public)属性。如my book的10.4.3节所述,如果温莎具有匹配的组件,并且该匹配不区分大小写,它将尝试为每个可写属性分配一个值。当您将名为
request
的HttpRequestMessage传递给Resolve方法时,就会发生这种情况,因此您需要告诉CaSTLe Windsor它应该放弃ApiControllers的属性注入(inject)。这是我在基于约定的注册中这样做的方式:container.Register(Classes
.FromThisAssembly()
.BasedOn<IHttpController>()
.ConfigureFor<ApiController>(c => c.Properties(pi => false))
.LifestyleTransient());