ASP Web Api - IoC - разрешить HttpRequestMessage

Я пытаюсь настроить Castle Windsor с помощью ASP.NET WebAPI.

Я также использую пакет Hyprlinkr (https://github.com/ploeh/Hyprlinkr), поэтому нужен экземпляр HttpRequestMessage, введенный в одну из зависимостей мой контроллер.

Я следую этой статье Марк Seemann - http://blog.ploeh.dk/2012/04/19/WiringHttpControllerContextWithCastleWindsor.aspx, но я нахожу, что хотя API работает, когда я делаю вызовите его, запрос просто зависает. Нет сообщения об ошибке. Его как бы в бесконечном цикле. Его висит на вызове Resolve в моем Custom ControllerActivator

Я думаю, что некоторые из моих регистраций в замке ошибаются. Если я удалю те, о которых упоминалось в статье выше, то я могу успешно сделать вызов 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);
    }
}

ИЗМЕНИТЬ *********** ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ ****************

Итак, я настроил сценарий, когда контроллер просто принимает 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;

то есть. когда объект anon имеет свойство "запрос", а контроллер ctor arg называется "запрос". Это как-то заставляет контроллер думать, что свойство request имеет значение null. Это и вызывает ошибку, которую я вижу:

Невозможно повторно использовать экземпляр ApiController. "ApiController" должен быть для каждого входящего сообщения. Проверьте свой обычай "IHttpControllerActivator" и убедитесь, что он не будет производить тот же экземпляр.

в System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancelationToken) в System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage просьба, аннулирование отмены бронированияToken) в System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage запрос, Отмена Отмена бронированияТокнуть)

прочитайте это Как я могу обогатить композицию объекта в StructureMap, не вызывая инъекцию установщика?

Это объясняет аналогичный сценарий.

Конечно, hyprlinkr имеет это ctor arg для HttpRequestMessage, называемого "request", поэтому мне нужно указать объект anon с этим именем свойства.

Любые идеи?

Ответ 1

Здесь Корень Композиции, который работает для меня:

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 имеет общедоступное свойство с именем Request типа HttpRequestMessage. Как объясняется в разделе 10.4.3 моей книги Windsor попытается присвоить значение каждому записываемому свойству, если он имеет соответствующий компонент, - и это совпадение не учитывает регистр.

Когда вы передаете метод HttpRequestMessage с именем Request методу Resolve, это именно то, что происходит, поэтому вам нужно сообщить Castle Windsor, что он должен отказаться от Injection для ApiControllers. Вот как я нахожу это в регистрации на основе конвенции:

container.Register(Classes
    .FromThisAssembly()
    .BasedOn<IHttpController>()
    .ConfigureFor<ApiController>(c => c.Properties(pi => false))
    .LifestyleTransient());

Ответ 2

Почему бы не использовать уже встроенный механизм в ASP.NET Web API - распознаватель зависимостей

http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver

В проекте WebApiContrib реализована реализация резольвера CastleWindsor, но, как я видел, с преобразователем зависимостей

https://github.com/WebApiContrib/WebApiContrib.IoC.CastleWindsor

И как сказал Марк в комментарии - один из способов реализации IHttpControllerActivator

http://blog.ploeh.dk/2012/10/03/DependencyInjectionInASPNETWebAPIWithCastleWindsor.aspx