Подтвердить что ты не робот

Разрешение HttpControllerContext с замком Виндзор

В ASP.NET Web API экземпляры HttpControllerContext предоставляют много информации о текущей среде, включая URI текущего запроса.

Если служба полагается на такую ​​информацию (например, URI запроса), она должна быть доступна для ввода этой информации в службу.

Это довольно просто сделать, используя "Бедный человек": просто реализовать пользовательский 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 (замок Windsor IDependencyResolver, а не ASP.NET Web API IDependencyResolver):

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

Мне интересно, если это лучшее решение этой проблемы, или если есть лучший/более простой способ?

4b9b3361

Ответ 1

Как раз для полноты, ответ, который я получил от Кшиштофа Комича (нынешнего сопровождающего замка Виндзора) в Twitter, показал, что метод, изложенный в вопросе, действительно является правильным способом достижения этой конкретной цели.

(Тем не менее, я не могу ссылаться на этот твит, так как учетная запись Krzysztof twitter защищена (твиты не являются общедоступными.))

Ответ 2

Мне кажется, что ваш InlineDependenciesPropagatingDependencyResolver фактически маскирует что-то достаточно критичное для архитектуры вашего приложения: один или несколько из ваших компонентов имеют зависимости, которые нельзя надежно разрешить статически, из контейнера или из динамического контекста.

Это нарушает предположение, которое большинство разработчиков сделало бы при передаче встроенных зависимостей Resolve() (которые они получают только на одном уровне разрешения зависимостей), а в некоторых сценариях может привести к неправильной переопределению некоторой другой настроенной службы. (Например, если у вас был другой компонент с несколькими уровнями, у которого была зависимость одного и того же типа и имени). Это может быть потенциальной причиной ошибок, которые было бы очень трудно идентифицировать.

Проблема, лежащая в основе этого, является сложной для DI и на самом деле указывает на то, что IoC не является реально выполнимым (т.е. наша зависимость (и) должна быть "нажата" и не может быть "вытащена" для нас контейнером), Мне кажется, есть два варианта:

1) исправить проблему, которая предотвращает "инверсию". то есть оберните HttpControllerContext/HttpContext, добавьте, что оболочка будет вести себя по мере необходимости в самообслуживаемом сценарии, и ваши компоненты полагаются на эту оболочку, а не на HttpControllerContext/HttpContext напрямую.

2) отражают недостатки среды, с которой вы работаете (что она не полностью поддерживает "инверсию" ), и сделайте обходной путь для устранения этих недостатков очень явным. В вашем сценарии это, вероятно, связано с использованием типизированного factory (интерфейса) для создания экземпляра компонента, требующего baseUri в вашем IHttpControllerActivator.Create(). Это означало бы, что если бы этот компонент был дальше вниз по иерархии зависимостей, вам нужно было бы явно создать иерархию зависимостей до тех пор, пока у вас не будет ваш контроллер.

Я бы, вероятно, пошел на второй вариант просто потому, что, когда соглашения не сокращают его, я предпочитаю быть максимально явным.

ОБНОВЛЕНО Предполагая, что у нас был тип контроллера A, который основывался на компоненте B, который в свою очередь полагался на baseUri, второй вариант может выглядеть примерно так:

// Typed factories for components that have dependencies, which cannot be resolved statically
IBFactory bFactory; 
IAFactory aFactory;

public IHttpController Create(HttpControllerContext controllerContext, Type controllerType)
{
    if (controllerType == typeof(A))
    {
        // Special handling for controller where one or more dependencies
        // are only available via controllerContext.
        var baseUri = new Uri(controllerContext.Request.RequestUri.GetLeftPart(UriPartial.Authority));
        B b = this.bFactory.Create(baseUri);
        return this.aFactory.Create(b);
    }
    // Default for all other controllers 
    return (IHttpController)this.container.Resolve(controllerType);
}

Ключевыми моментом является то, что это явно связано с недостатками нашей среды, оно связывает затронутые типы специально с переопределениями зависимостей, которые мы предоставляем императивно, и гарантирует, что мы не случайно переопределяем любые другие зависимости.