Problèmes d’autofac

J’essaie de faire fonctionner l’autofac, mais j’ai des problèmes avec mes classes unitofwork / user manager.

Au départ, je définissais mon unité de travail en tant qu’instance par demande, comme suit:

builder.RegisterType<UnitOfWork>().As().InstancePerRequest(); 

Mais dans mon StartupConfig.cs, j’essayais de configurer oAuth comme ceci:

 private static OAuthAuthorizationServerOptions ConfigureOAuthTokenGeneration(IAppBuilder app, ILifetimeScope scope) { var t = scope.Resolve(); // Get our providers var authProvider = scope.Resolve(); var refreshTokenProvider = scope.Resolve(); // Create our OAuth options return new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, // TODO: Remove this line TokenEndpointPath = new PathSsortingng("/oauth/access_token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), AccessTokenFormat = new Business.Authentication.JwtFormat("http://localhost:62668"), Provider = authProvider, RefreshTokenProvider = refreshTokenProvider }; } 

La scope est obtenue par ceci:

 var scope = config.DependencyResolver.GetRootLifetimeScope(); 

Pour cette raison, je ne pouvais pas utiliser InstancePerRequest pour UnitOfWork , je l’ai remplacé par ceci:

 builder.RegisterType<UnitOfWork>().As().InstancePerLifetimeScope(); 

Maintenant, l’application fonctionne réellement, mais j’obtiens une nouvelle erreur avec UserProvider , elle est instanciée comme ceci:

 builder.RegisterType().As().InstancePerRequest(); 

Mais si je lance ça, j’obtiens cette erreur:

Aucune étendue avec une balise correspondant à ‘AutofacWebRequest’ n’est visible à partir de l’étendue dans laquelle l’instance a été demandée.

Si vous voyez ceci pendant l’exécution d’une application Web, cela indique généralement qu’un composant enregistré en tant que requête HTTP est demandé par un composant SingleInstance () (ou un scénario similaire). Sous l’intégration Web, demandez toujours des dépendances à partir du résolveur de dépendances ou de la scope de la durée de vie de la demande, jamais du conteneur lui-même.

Ceci est en fait invoqué par la ligne:

 var authProvider = scope.Resolve(); 

qui est dans mon StartupConfig.cs . OAuthProvider a besoin de UserProvider , la signature ressemble à ceci:

 public OAuthProvider(IAdvancedEncryptionStandardProvider helper, IUserProvider userProvider) { this._helper = helper; this._userProvider = userProvider; } 

Donc, comme cela ne fait pas partie de la “demande”, j’ai modifié le fournisseur UserProvider afin qu’il soit résolu comme suit :

 builder.RegisterType().As().InstancePerLifetimeScope(); 

qui correspond à la UnitOfWork maintenant, le projet va charger. Mais si j’ai une interface qui essaie de faire 2 choses (obtenir l’utilisateur actuel et lister tous les utilisateurs), elle crée 2 requêtes, toutes deux créant une nouvelle instance de UserController :

 public UsersController(IUserProvider provider) { this._provider = provider; } 

qui à son tour essaie de créer 2 instances de UserProvider . Cela jette une erreur:

Le contexte ne peut pas être utilisé pendant la création du modèle. Cette exception peut être levée si le contexte est utilisé dans la méthode OnModelCreating ou si la même instance de contexte est utilisée simultanément par plusieurs threads. Notez que les membres d’instance de DbContext et les classes associées ne sont pas garantis d’être thread-safe.

Donc, je suppose que j’ai besoin de savoir comment résoudre ce problème. C’est comme si j’avais besoin de deux champs d’application, un pour le début de l’application et un autre pour tout le rest. Est-ce que quelqu’un peut m’aider avec ça?

Le problème

Dans la mesure où, au moment de l’enregistrement du middleware OWIN, vous devez fournir une instance de OAuthAuthorizationServerOptions , il est impossible de résoudre les propriétés Provider et RefreshTokenProvider par requête HTTP.

Ce qu’il nous faut, c’est un moyen de créer les OAuthAuthorizationServerOptions par requête HTTP. Par extension, le même concept s’appliquerait à OAuthAuthorizationServerMiddleware .

Une solution possible

C’est exactement ce que fait AutofacMiddleware ; Il encapsule un middleware OWIN en le résolvant pendant la requête HTTP à partir de la scope de la durée de vie stockée dans le contexte OWIN, puis en l’exécutant. Cela signifie que nous pouvons maintenant instancier un nouveau OAuthAuthorizationServerMiddleware pour chaque requête HTTP.

Comme expliqué dans la documentation , lorsque vous utilisez app.UseAutofacMiddleware(container) dans votre classe de Startup , Autofac fait 2 choses :

  • il se connecte au pipeline OWIN pour créer une étendue de durée de vie nestede pour chaque requête HTTP
  • il OwinMiddleware tous les services OwinMiddleware inscrits dans le conteneur avec AutofacMiddleware et les ajoute au pipeline OWIN

La solution consiste ensuite à enregistrer OAuthAuthorizationServerMiddleware et toutes ses dépendances dans le conteneur Autofac. Il sera automatiquement résolu pour chaque demande et exécuté.

OAuthAuthorizationServerMiddleware a 3 dépendances:

  • Le prochain middleware OWIN du pipeline, AutofacMiddleware prend en charge car il le fournit à la méthode ResolveTypedParameter.From(this.Next)
  • Une instance de IAppBuilder
  • L’instance OAuthAuthorizationServerOptions

Nous devons enregistrer les deux dernières dépendances plus le middleware lui-même dans le conteneur. Voyons à quoi cela pourrait ressembler:

Disclaimer: Je n’ai pas essayé le code ci-dessous

 // Here go all the registrations associated with the `Provider` // and `RefreshTokenProvider` properties with the appropriate lifetimes builder .RegisterInstance(app) .As(); builder .Register(x => new OAuthAuthorizationServerOptions { AllowInsecureHttp = true, // TODO: Remove this line TokenEndpointPath = new PathSsortingng("/oauth/access_token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), AccessTokenFormat = new Business.Authentication.JwtFormat("http://localhost:62668"), Provider = x.Resolve(), RefreshTokenProvider = x.Resolve() }) .AsSelf() // InstancePerDependency is same as InstancePerLifetimeScope // in this case since the middleware will get resolved exactly one // time per HTTP request anyway .InstancePerDependency(); builder.RegisterType(); 

Contrôler l’ordre du middleware

Bien que cela puisse fonctionner, il est possible que cela ne réponde pas à vos besoins puisque le middleware OAuth sera enregistré dans le pipeline OWIN où vous appelez app.UseAutofacMiddleware(container) .

Si vous souhaitez davantage de contrôle sur l’ordre du middleware, vous pouvez séparer la création de la scope de la durée de vie Autofac de l’enregistrement du middleware dans le pipeline OWIN :

Disclaimer: Je n’ai pas essayé le code ci-dessous

 // creates the per HTTP request lifetime scope app.UseAutofacLifetimeScopeInjector(container); // "usual" OWIN middlewares registrations app.UseFoo(); // now use one from the container app.UseMiddlewareFromContainer(); // other "usual" OWIN middlewares registrations app.UseBar();