В веб-API ASP.NETэкземпляры HttpControllerContext предоставляют много информации о текущей среде, включая URI текущего запроса.
Если служба полагается на на такую информацию (например, URI запроса), должна быть возможность ввести эту информацию в службу.
Это довольно легко сделать с помощью DI Poor Man: просто реализуйте собственный IHttpControllerActivator.
Однако с замком Виндзор это внезапно становится очень трудным.Ранее я описал очень запутанный способрешения этой проблемы, но он зависит от образа жизни PerWebRequest, и оказывается, что этот образ жизни не работает в сценариях самостоятельного хостинга, потому что HttpContext.Current пусто.
До сих пор мне удавалось заставить это работать, передавая нужную информацию в качестве встроенного аргумента в метод Resolve из пользовательского IHttpControllerActivator:
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 Castle Windsor, а не IDependencyResolver веб-API ASP.NET):
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());
Мне интересно, это лучшее решение этой проблемы или есть лучший/более простой способ?