Autofac - Impossibile creare l'ambito della durata della richiesta perché HttpContext non è disponibile - a causa di codice asincrono?

Autofac - Impossibile creare l'ambito della durata della richiesta perché HttpContext non è disponibile - a causa di codice asincrono?

AGGIORNAMENTO 20 novembre 2014: Nelle versioni di Autofac.Mvc5 da quando questa domanda è stata rilasciata, l'implementazione di AutofacDependencyResolver.Current è stato aggiornato per eliminare la necessità di un HttpContext . Se stai riscontrando questo problema e hai trovato questa risposta, puoi potenzialmente risolvere facilmente le cose aggiornando a una versione successiva di Autofac.Mvc5 . Tuttavia, lascerò intatta la risposta originale affinché le persone capiscano perché il richiedente originale aveva problemi.

Segue la risposta originale:

AutofacDependencyResolver.Current richiede un HttpContext .

Scorrendo il codice, AutofacDependencyResolver.Current assomiglia a questo:

public static AutofacDependencyResolver Current
{
  get
  {
    return DependencyResolver.Current.GetService<AutofacDependencyResolver>();
  }
}

E, naturalmente, se l'attuale risolutore di dipendenze è un AutofacDependencyResolver poi proverà a risolvere...

public object GetService(Type serviceType)
{
  return RequestLifetimeScope.ResolveOptional(serviceType);
}

Che ottiene l'ambito di durata da un RequestLifetimeScopeProvider ...

public ILifetimeScope GetLifetimeScope(Action<ContainerBuilder> configurationAction)
{
  if (HttpContext.Current == null)
  {
    throw new InvalidOperationException("...");
  }

  // ...and your code is probably dying right there so I won't
  // include the rest of the source.
}

Deve funzionare in questo modo per supportare strumenti come Glimpse che avvolgono/proxy dinamicamente il risolutore di dipendenze per strumentarlo. Ecco perché non puoi semplicemente trasmettere DependencyResolver.Current as AutofacDependencyResolver .

Praticamente qualsiasi cosa che utilizzi il Autofac.Integration.Mvc.AutofacDependencyResolver richiede HttpContext .

Ecco perché continui a ricevere questo errore. Non importa se non hai dipendenze registrate InstancePerHttpRequest - AutofacDependencyResolver richiederà comunque un contesto web.

Immagino che l'altra app per il flusso di lavoro che avevi in ​​cui questo non era un problema fosse un'app MVC o qualcosa in cui c'era sempre un contesto web.

Ecco cosa consiglierei:

  • Se devi utilizzare componenti al di fuori di un contesto web e sei in WebApi, usa il Autofac.Integration.WebApi.AutofacWebApiDependencyResolver .
  • Se sei in WCF, utilizza lo standard AutofacHostFactory.Container e che ospita l'implementazione della fabbrica per risolvere le dipendenze. (WCF è un po' strano con il suo potenziale host singleton, ecc., quindi "per richiesta" non è così semplice.)
  • Se hai bisogno di qualcosa di "agnostico" della tecnologia, considera il CommonServiceLocator implementazione per AutoFac. Non crea richieste di durata, ma può risolvere alcuni problemi.

Se mantieni queste cose chiare e non provi a utilizzare i vari risolutori al di fuori dei loro habitat nativi, per così dire, non dovresti incorrere in problemi.

Tu puoi in modo abbastanza sicuro usa InstancePerApiRequest e InstancePerHttpRequest in modo intercambiabile nelle registrazioni dei servizi. Entrambe queste estensioni utilizzano lo stesso tag dell'ambito di durata, quindi la nozione di richiesta Web MVC e una richiesta API Web possono essere trattate in modo simile anche se l'ambito di durata sottostante in un caso è basato su HttpContext e l'altro è basato su IDependencyScope . Quindi potresti ipoteticamente condividere un modulo di registrazione tra app/tipi di app e dovrebbe fare la cosa giusta.

Se hai bisogno del contenitore Autofac originale, memorizza il tuo riferimento ad esso. Anziché presumere che Autofac restituirà quel contenitore in qualche modo, potrebbe essere necessario memorizzare un riferimento al contenitore dell'applicazione se è necessario recuperarlo in un secondo momento per qualsiasi motivo.

public static class ApplicationContainer
{
  public static IContainer Container { get; set; }
}

// And then when you build your resolvers...
var container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver =
  new AutofacWebApiDependencyResolver(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
ApplicationContainer.Container = container;

Questo ti farà risparmiare un sacco di problemi lungo la strada.


Le mie ipotesi:

  1. Stai eseguendo un progetto di flusso di lavoro in un Thread/AppDomain separato dal progetto MVC.
  2. IUserRepo dipende da HttpContext

Se la mia ipotesi fosse corretta, il progetto Workflow non avrebbe idea di HttpContext.Current .

Il progetto WindowsWorkflow viene eseguito sempre (se ho capito bene, in realtà non funzionava con questa tecnologia). Dove come MVC si basa su richieste HTTP. HttpContext.Current viene popolato solo quando c'è una richiesta in arrivo. Se nessuna richiesta, questa variabile è nulla. Cosa succede se non c'è alcuna richiesta, ma l'istanza del flusso di lavoro sta tentando di accedere a HttpContext ? Corretto - eccezione di riferimento nullo. O nel tuo caso eccezione per la risoluzione delle dipendenze.

Cosa devi fare:

  1. Separa le registrazioni dei container in moduli:modulo di dominio per tutte le tue classi di dominio. Quindi modulo MVC:per tutte le tue specifiche MVC, come User.Current o HttpContext.Current . E il modulo Flusso di lavoro (se richiesto) con tutte le implementazioni specifiche del flusso di lavoro.
  2. Al momento dell'inizializzazione del flusso di lavoro, crea un contenitore autofac con moduli di dominio e flusso di lavoro, escludi le dipendenze MVC. Per il contenitore MVC:crealo senza modulo flusso di lavoro.
  3. Per IUserRepo creare un'implementazione che non dipende da HttpContext. Probabilmente sarà la cosa più problematica da fare.

Ho fatto qualcosa di simile per l'esecuzione di Quartz.Net in Azure. Vedi il mio post sul blog su questo:http://tech.trailmax.info/2013/07/quartz-net-in-azure-with-autofac-smoothness/. Questo post non ti aiuterà direttamente, ma spiega il mio ragionamento per dividere i moduli autofac.

Aggiorna come da commento: WebApi chiarisce molte cose qui. La richiesta WebApi non passa attraverso la stessa pipeline delle richieste MVC. E i controller WebApi non hanno accesso a HttpContext. Vedi questa risposta.

Ora, a seconda di cosa stai facendo nel tuo controller wepApi, potresti voler cambiare il IUserRepo implementazione per poter lavorare sia con MVC che con WebApi.