Autofac – L’étendue de la durée de vie de la demande ne peut pas être créée car le HttpContext n’est pas disponible – en raison d’un code async

Question courte: Identique à ce problème sans réponse

Longue question:

Je viens de transférer du code d’une solution Web Api MVC 4 + qui utilisait Autofac dans ma nouvelle solution, qui utilise également Autofac mais uniquement avec Web Api 2 (pas de projet MVC 5.1, mais simplement une API Web).

Dans ma solution précédente, j’avais MVC4 et Web Api, donc j’avais 2 fichiers Bootstrapper.cs, un pour chacun. J’ai copié uniquement le programme d’amorçage Web Api pour le nouveau projet.

Maintenant, j’ai 2 autres projets dans la nouvelle solution qui doivent tirer une dépendance. Supposons simplement que je dois utiliser DependencyResolver.Current.GetService() bien qu’il s’agisse d’un anti-motif.

Au début, cela ne fonctionnait pas tant que je n’avais pas réglé le résolveur de dépendances MVC sur le même conteneur:

 GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); //I had to pull in Autofac.Mvc and Mvc 5.1 integration but this line fixed it DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 

Ce qui est étrange, c’est de ne le fixer que dans UN de ces projets! Voici la situation:

  Solution.Web project Bootstrapper.cs that registers both dependency resolvers for web api and mvc. Solution.ClassLib project var userRepo = DependencyResolver.Current.GetService(); //Good! :) Solution.WindowsWorkflow project var userRepo = DependencyResolver.Current.GetService(); //Throws exception :( 

L’exception est la suivante: la scope de la durée de vie de la demande ne peut pas être créée car le HttpContext n’est pas disponible.

Maintenant, avant de commencer à blâmer le stream de travail, sachez que cette configuration exacte fonctionnait parfaitement dans une autre solution: le stream de travail pouvait parfaitement utiliser DependencyResolver. Donc, je suppose que cela est dû à l’utilisation d’une version plus récente d’Autofac et au fait que le stream de travail s’exécute de manière asynchrone (tout comme la question à laquelle j’ai lié en ce qui concerne le code async.)

J’ai essayé de changer tout le code d’enregistrement pour utiliser InstancePerLifetimeScope() au lieu de InstancePerHttpRequest() et d’essayer de créer une scope:

 using (var c= AutofacDependencyResolver.Current .ApplicationContainer.BeginLifetimeScope("AutofacWebRequest")) { var userRepo = DependencyResolver.Current.GetServices(); } 

Mais cela n’a pas changé l’exception. Décomposant le code encore plus loin voici le coupable exact:

 var adr = AutofacDependencyResolver.Current; //Throws that exception 

Vraiment besoin de passer outre cela a passé trop de temps bloqué. Récompensera la réponse existante avec une prime en 2 jours

MISE À JOUR 20 novembre 2014: Dans les versions d’ Autofac.Mvc5 depuis la Autofac.Mvc5 cette question, l’implémentation d’ AutofacDependencyResolver.Current a été mise à jour pour supprimer le besoin d’un HttpContext . Si vous rencontrez ce problème et avez trouvé cette réponse, vous pouvez éventuellement résoudre facilement les problèmes en Autofac.Mvc5 une mise à jour vers une version ultérieure d’ Autofac.Mvc5 . Cependant, je laisserai la réponse originale intacte pour permettre aux gens de comprendre pourquoi le demandeur de la question initiale posait problème.

La réponse originale suit:


AutofacDependencyResolver.Current requirejs un HttpContext .

En parcourant le code, AutofacDependencyResolver.Current ressemble à ceci:

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

Et, bien sûr, si le résolveur de dépendances actuel est un AutofacDependencyResolver il essaiera de faire une résolution …

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

Ce qui obtient la scope de la durée de vie d’un RequestLifetimeScopeProvider

 public ILifetimeScope GetLifetimeScope(Action 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. } 

Cela doit fonctionner comme ça pour supporter des outils comme Glimpse qui encapsulent / résolvent dynamicment le résolveur de dépendances afin de l’instrumenter. C’est pourquoi vous ne pouvez pas simplement DependencyResolver.Current as AutofacDependencyResolver .

Pratiquement tout ce qui utilise Autofac.Integration.Mvc.AutofacDependencyResolver requirejs HttpContext .

C’est pourquoi vous continuez à avoir cette erreur. Peu importe si vous n’avez aucune dépendance enregistrée. InstancePerHttpRequestAutofacDependencyResolver nécessitera toujours un contexte Web.

J’imagine que l’autre application de stream de travail que vous aviez ne posant pas problème était une application MVC ou quelque chose dans laquelle il y avait toujours un contexte Web.

Voici ce que je recommanderais:

  • Si vous devez utiliser des composants en dehors d’un contexte Web et que vous vous trouvez dans WebApi, utilisez le Autofac.Integration.WebApi.AutofacWebApiDependencyResolver .
  • Si vous êtes dans WCF, utilisez le AutofacHostFactory.Container standard AutofacHostFactory.Container et cette implémentation de fabrique d’hôtes pour résoudre les dépendances. (WCF est un peu bizarre avec son potentiel d’hôte singleton, etc., donc “à la demande” n’est pas aussi simple.)
  • Si vous avez besoin de quelque chose de “agnostique” de technologie, considérez l’implémentation de CommonServiceLocator pour Autofac. Cela ne crée pas de durée de vie de demande, mais cela peut résoudre certains problèmes.

Si vous maintenez ces choses au clair et n’essayez pas d’utiliser les divers résolveurs en dehors de leur habitat natal, pour ainsi dire, vous ne devriez pas vous heurter à des problèmes.

Vous pouvez utiliser en toute sécurité InstancePerApiRequest et InstancePerHttpRequest manière interchangeable dans les enregistrements de service. Ces deux extensions utilisent la même balise de durée de vie, de sorte que la notion de demande Web MVC et de demande d’API Web peut être traitée de la même manière, même si la scope de la vie sous-jacente est basée sur HttpContext et l’autre sur IDependencyScope . Vous pouvez donc hypothétiquement partager un module d’enregistrement sur plusieurs types d’applications et il devrait faire ce qui est bien.

Si vous avez besoin du conteneur Autofac d’origine, enregistrez votre propre référence. Plutôt que de supposer qu’Autofac renverra ce conteneur d’une manière ou d’une autre, vous devrez peut-être stocker une référence dans votre conteneur d’application si vous avez besoin de l’obtenir plus tard, quelle qu’en soit la raison.

 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; 

Cela vous évitera beaucoup de problèmes par la suite.

Mes hypothèses:

  1. Vous exécutez un projet de stream de travail dans un projet Thread / AppDomain from MVC distinct.
  2. IUserRepo dépend de HttpContext

Si mon hypothèse est correcte, le projet de workflow n’aura aucune idée de HttpContext.Current .

Le projet WindowsWorkflow s’exécute tout le temps (si je comprends bien – ne fonctionnait pas réellement avec cette technologie). MVC est basé sur des requêtes HTTP. HttpContext.Current est HttpContext.Current uniquement lorsqu’une demande arrive. Si aucune demande, cette variable est null. Que se passe-t-il s’il n’y a pas de demande, mais que l’instance de stream de travail tente d’accéder à HttpContext ? Correct – exception de référence nulle. Ou dans votre exception de résolution de dépendance de cas.

Qu’as tu besoin de faire:

  1. Séparez les inscriptions de conteneurs en modules – module de domaine pour toutes vos classes de domaine. Ensuite, module MVC: pour toutes vos spécificités MVC, comme User.Current ou HttpContext.Current . Et module de workflow (si nécessaire) avec toutes les implémentations spécifiques de workflow.
  2. Lors de l’initialisation du workflow, créez un conteneur autofac avec des modules de domaine et de workflow, excluez les dépendances MVC. Pour le conteneur MVC – créez-le sans module de stream de travail.
  3. Pour IUserRepo créez une implémentation indépendante de HttpContext. Ce sera probablement le plus problématique à faire.

J’ai fait quelque chose de similaire pour l’exécution de Quartz.Net dans Azure. Voir mon article de blog à ce sujet: http://tech.trailmax.info/2013/07/quartz-net-in-azuree-with-autofac-foothness / . Cet article ne vous aidera pas directement, mais explique mon raisonnement en faveur de la scission des modules autofac.

Mise à jour selon le commentaire: WebApi clarifie beaucoup de choses ici. Les demandes WebApi ne passent pas par le même pipeline que vos demandes MVC. Et les contrôleurs WebApi n’ont pas access à HttpContext. Voir cette réponse .

Maintenant, en fonction de ce que vous faites dans votre contrôleur wepApi, vous voudrez peut-être modifier l’implémentation IUserRepo pour pouvoir utiliser à la fois MVC et WebApi.

Nous sums actuellement dans une situation où nous avons des tests qui souffrent du problème de «manque de httpcontext» mais ne peuvent pas encore utiliser les excellentes recommandations ci-dessus en raison de contraintes de version.

Notre solution consistait à créer un contexte http “simulé” dans notre configuration de test: voir: Mock HttpContext.Current dans la méthode Test Init