10

Sto cercando di impostare Castle Windsor con ASP.NET WebAPI.ASP Web Api - IoC - Risolvi HttpRequestMessage

Inoltre sto usando il pacchetto Hyprlinkr (https://github.com/ploeh/Hyprlinkr) e quindi hanno bisogno di un'istanza di HttpRequestMessage iniettato a una delle dipendenze di mio controller.

Sto seguendo questo articolo di Mark Seemann - http://blog.ploeh.dk/2012/04/19/WiringHttpControllerContextWithCastleWindsor.aspx, ma sto trovando che anche se l'API viene eseguita, quando faccio una chiamata, la richiesta si blocca. Nessun messaggio di errore. È come se fosse in un ciclo infinito. È in attesa della chiamata a Risolvi il mio controller personalizzato. Attivatore

Sto pensando di avere alcune delle mie registrazioni di Castello errate. Se rimuovo quelli menzionati nell'articolo sopra, allora posso effettuare con successo una chiamata all'API (anche se senza le dipendenze ho bisogno di essere risolto)

Qualche idea?

codice è al di sotto

//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); 
    } 
} 

EDIT *********** INFORMAZIONI AGGIUNTIVE ****************

Così ho creato uno scenario in cui il controllore vuole solo un HttpRequestMessage come argomento ctor e ha trovato:

Questo funziona:

//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; 

Tuttavia, questo non.

//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; 

cioè quando l'oggetto anon ha una proprietà chiamata "richiesta" e arg controllore ctor è chiamato "richiesta". In qualche modo, il controllore pensa che la proprietà della richiesta sia nulla. Qual è la causa dell'errore che vedo:

Impossibile riutilizzare un'istanza 'ApiController'. 'ApiController' deve essere costruito per messaggio in arrivo. Controlla il tuo custom 'IHttpControllerActivator' e assicurati che non produca la stessa istanza. .

a System.Web.Http.ApiController.ExecuteAsync (HttpControllerContext controllerContext, CancellationToken CancellationToken) a System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal (HttpRequestMessage richiesta, CancellationToken CancellationToken) a System.Web.Http .Dispatcher.HttpControllerDispatcher.SendAsync (HttpRequestMessage richiesta, CancellationToken CancellationToken)

hanno una lettura di questo How can I enrich object composition in StructureMap without invoking setter injection?

Spiega uno scenario simile.

Ovviamente, hyprlinkr ha il suo argomento per HttpRequestMessage chiamato "richiesta", quindi è necessario specificare l'oggetto anon con tale nome di proprietà.

Qualche idea?

+3

Il post collegato a sopra descrive come utilizzare Castle Windsor con un'anteprima dell'API Web. Poiché vi sono state interruzioni delle modifiche tra l'anteprima e la versione RTM, l'approccio descritto non funziona più. Si prega di fare riferimento a http://blog.ploeh.dk/2012/10/03/DependencyInjectionInASPNETWebAPIWithCastleWindsor.aspx per una descrizione di come rendere DI lavorare con Castle Windsor in Web API RTM. –

+0

grazie - sì - l'ho seguito. Semplicemente non capisco come fare la registrazione per HttpRequestMessage di cui hyprlinkr ha bisogno. Puoi indicarmi la giusta direzione? – ChrisCa

+0

In questo modo: http://stackoverflow.com/q/10854701/126014 –

risposta

12

Ecco una radice Composizione che funziona per me:

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

Ecco come ho creato il contenitore:

this.container = 
    new WindsorContainer(
     new DefaultKernel(
      new InlineDependenciesPropagatingDependencyResolver(), 
      new DefaultProxyFactory()), 
     new DefaultComponentInstaller()) 
     .Install(new MyWindsorInstaller()); 

ed ecco l'InlineDependenciesPropagatingDependencyResolver:

public class InlineDependenciesPropagatingDependencyResolver : 
    DefaultDependencyResolver 
{ 
    protected override CreationContext RebuildContextForParameter(
     CreationContext current, 
     Type parameterType) 
    { 
     if (parameterType.ContainsGenericParameters) 
     { 
      return current; 
     } 

     return new CreationContext(parameterType, current, true); 
    } 
} 

Infine, ecco come posso registrarmi RouteLinker:

container.Register(Component 
    .For<RouteLinker, IResourceLinker>() 
    .LifestyleTransient()); 

Una cosa da essere consapevoli è che la classe base ApiController ha una proprietà pubblica denominata Request del tipo HttpRequestMessage. Come spiegato nella sezione 10.4.3 di my book, Windsor tenterà di assegnare un valore a ciascuna proprietà scrivibile se ha un componente corrispondente e quella corrispondenza è insensibile al maiuscolo/minuscolo.

Quando si passa un HttpRequestMessage denominato request al metodo Resolve, questo è esattamente ciò che accade, quindi è necessario comunicare a Castle Windsor che deve rinunciare a Property Injection per ApiControllers. Ecco come lo indosso in una registrazione basata su convenzione:

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

grazie - devi sostituire IHttpeControllerActivator? GlobalConfiguration.Configuration.Services.Replace (typeof (IHttpControllerActivator), nuovo WindsorCompositionRoot (contenitore)); Se non lo faccio, ricevo un errore che il mio controller non ha un costruttore predefinito, quindi ovviamente non è stato costruito da castle. Se aggiungo la riga sopra, ho lo stesso errore di prima. Ad esempio, non è possibile riutilizzare un'istanza "ApiController". Puoi mostrarmi come si presenta la registrazione del controller? Ho questo: container.Register (Component.For (). LifestyleTransient()); – ChrisCa

+0

Fare riferimento a http://blog.ploeh.dk/2012/10/03/DependencyInjectionInASPNETWebAPIWithCastleWindsor.aspx per informazioni di base. Non è necessario (e non è possibile) registrare HttpRequestMessage esattamente perché c'è una nuova istanza per ogni invocazione di IHttpControllerActivator.Create. La registrazione per ValuesController sembra a posto ... Dalle informazioni attualmente disponibili, non posso dire cosa c'è che non va. –

+0

Sì, è molto strano. Forse ho una versione diversa della web api per te? C'è un modo per dire? Inoltre, puoi confermare se la tua versione sostituisce l'IHttpControllerActivator predefinito i.e config.Services.Replace (typeof (IHttpControllerActivator), nuovo WindsorCompositionRoot (container)); – ChrisCa

0

Perché non utilizzare il meccanismo incorporato già in ASP.NET Web API - Dipendenza resolver

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

Nel progetto WebApiContrib c'è implementazione resolver CastleWindsor, ma come ho visto con Dependency resolver

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

E come Mark ha detto in commento - uno dei modi per implementare IHttpControllerActivator

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

+0

Sono più interessato a scoprire come risolvere HttpRequestMessage. In entrambi gli approcci ... – ChrisCa

+0

HttpRequestMessage risolto ... hm, perché ne hai bisogno? – Regfor

+0

la libreria Hyprlinkr ha una dipendenza da costruttore che devo soddisfare – ChrisCa