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. InstancePerHttpRequest
– AutofacDependencyResolver
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:
Autofac.Integration.WebApi.AutofacWebApiDependencyResolver
. 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 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:
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:
User.Current
ou HttpContext.Current
. Et module de workflow (si nécessaire) avec toutes les implémentations spécifiques de workflow. 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