Enregistrer IAuthenticationManager avec Simple Injector

Je suis en train de faire une configuration pour Simple Injector où j’ai déplacé tous mes enregistrements vers le pipeline OWIN

Maintenant, le problème est que j’ai un contrôleur AccountController qui prend réellement les parameters comme

 public AccountController( AngularAppUserManager userManager, AngularAppSignInManager signinManager, IAuthenticationManager authenticationManager) { this._userManager = userManager; this._signInManager = signinManager; this._authenticationManager = authenticationManager; } 

Maintenant, mes configurations Owin Pipeline ressemblent à ceci

 public void Configure(IAppBuilder app) { _container = new Container(); ConfigureOwinSecurity(app); ConfigureWebApi(app); ConfigureSimpleinjector(_container); app.Use(async (context, next) => { _container.Register(() => context); await next(); }); _container.Register( () => _container.GetInstance().Authentication); _container.Register<SignInManager, AngularAppSignInManager>(); } private static void ConfigureOwinSecurity(IAppBuilder app) { app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, CookieName = "AppNgCookie", //LoginPath = new PathSsortingng("/Account/Login") }); } private static void ConfigureWebApi(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); WebApiConfig.Register(config); app.UseWebApi(config); } private static void ConfigureSimpleinjector(Container container) { SimpleInjectorInitializer.Initialize(container); } 

Et Simple Injector Initializer ressemble à ceci

 private static void InitializeContainer(Container container) { container.Register(); container.Register<IUserStore, AngularAppUserStore>(); container.Register<IRoleStore, AngularAppRoleStore>(); container.Register<UserManager, AngularAppUserManager>(); container.Register<RoleManager, AngularAppRoleManager>(); //container.RegisterPerWebRequest<SignInManager, AngularAppSignInManager>(); container.Register<IdentityFactoryOptions, IdentityFactoryOptions>(); //container.Register(() => HttpContext.Current.GetOwinContext().Authentication); //container.Register<SignInManager, AngularAppSignInManager>(); // For instance: // container.Register(); } 

Le problème est maintenant le contrôleur ne peut pas enregistrer IAuthenticationManager . J’ai essayé d’utiliser

 container.Register( () => HttpContext.Current.GetOwinContext().Authentication); 

Mais cela me laisse avec Exception comme:

System.InvalidOperationException: Aucun élément owin.Environment n’a été trouvé dans le contexte.

Dans cette ligne

 container.Register( () => HttpContext.Current.GetOwinContext().Authentication); 

J’ai également essayé au lieu d’utiliser HttpContext.Current.GetOwinContext().Authentication avec la configuration mentionnée ci-dessus dans public void Configure(app) méthode public void Configure(app) pour s’enregistrer à l’aide de app.Use() . Et plus tard, résolvez-le via conteneur pour obtenir le IAuthenticationManager . Mais toutes les possibilités m’ont laissé échoué.

Qu’est-ce que j’oublie ici? Pourquoi HttpContext.Current.GetOwinContext().Authentcation ne parvient pas à résoudre authentcation à partir de OwinContext?

Et si ce n’est pas le cas, pourquoi la même configuration via app.Use ne fonctionne-t-elle pas non plus?

    Ce que vous faites avec l’enregistrement IAuthenticationManager fonctionné pour moi sans aucun problème. À un moment donné, j’ai eu la même exception que vous, mais cela a été causé par la ligne avec

     container.Verify(); 

    juste après la configuration du conteneur. Il essayait de créer toutes les instances d’objects enregistrés, mais il n’y avait aucun HttpContext.Current présent, d’où l’exception.

    N’obtenez-vous aucune instance du conteneur avant qu’une requête HTTP ne soit disponible? Si vous en avez vraiment besoin, le seul moyen de contourner ce problème est d’utiliser Factory, comme le suggère NightOwl888. Si vous n’avez pas besoin de conteneur avant la requête HTTP, refactorisez-le afin qu’il ne soit pas utilisé en dehors de la requête HTTP.

    Comme TrailMax l’a déjà mentionné, l’exception que vous avez probablement eue a été soulevée lors de l’appel de container.Verify() . Au moment du démarrage de l’application, il n’y a pas de HttpContext , d’où l’exception.

    Bien que la suppression de l’appel à container.Verify() résolve le problème, je vous déconseille de le faire et je suggérerai une meilleure solution ci-dessous.

    NightOwl888 fait référence à un vieil article de Mark Seemann (que je respecte beaucoup pour son travail sur DI). Dans cet article, Mark explique pourquoi il pense que la vérification du conteneur est inutile. Cet article semble toutefois obsolète et en conflit avec les articles plus récents de Mark. Dans un article plus récent, Mark explique que l’un des gros avantages de l’utilisation de Pure DI (c’est-à-dire l’dependency injection sans utiliser de conteneur DI) est de fournir le retour d’information le plus rapide possible sur l’exactitude . Mark et le rest d’entre nous valorisent évidemment les commentaires du compilateur et ceux des outils d’parsing de code statique, en tant que mécanisme de retour rapide. .Verify() Simple Injector et les services de diagnostic tentent de ramener ce retour rapide en retour. À mon avis, la méthode .Verify() Simple Injector prend en charge le travail que le compilateur ferait pour vous lorsqu’il effectue Pure DI et que les services de diagnostic constituent en un sens un outil d’parsing de code statique spécialisé dans votre configuration DI.

    S’il est en effet impossible pour un conteneur d’effectuer une vérification à 100% de sa configuration, cette vérification s’est tout de même avérée une pratique précieuse pour moi. Il serait stupide de penser qu’un simple appel à .Verify() résulterait en une application totalement exempte de bugs ou même en fonctionnement. Si quelqu’un peut penser que c’est ce que «vérifier» votre configuration DI signifie, je comprends pourquoi il arguerait du fait que cette fonctionnalité est sans valeur. Cela ressemble à une déclaration de truisme. Il n’y a pas de conteneur, y compris Simple Injector, qui prétend avoir une telle fonctionnalité.

    Vous êtes bien sûr toujours responsable de la rédaction des tests d’intégration et / ou unitaires, par exemple pour détecter si l’ordre des décorateurs appliqués est correct ou si toutes les implémentations de ISomeService sont effectivement enregistrées dans le conteneur.

    Je veux mentionner 2 arguments spécifiques du blog de Mark contre la vérification du conteneur.

    Il est facile de se retrouver dans une situation vérifiée par le conteneur, mais qui rest en panne au moment de l’exécution.

    Je suis d’accord avec cela, mais je pense que la documentation de Simple Injector contient de précieuses indications sur la façon de procéder.

    Lorsque vous faites la convention sur la configuration, il est facile d’obtenir des enregistrements qui ne devraient de toute façon pas figurer dans le conteneur.

    Je n’ai jamais eu ce problème, car j’estime que c’est une pratique saine d’empêcher de toute façon de se retrouver dans cette situation.

    Retour à la question:

    Bien que l’un des conseils de la documentation de Simple Injector consiste à utiliser des fabriques abstraites, je ne le ferais pas dans ce cas. Créer une usine pour quelque chose qui existe déjà me semble plutôt étrange. Peut-être que c’est juste un problème de nommage correct, mais pourquoi un AccountController aurait-il besoin d’un AuthenticationFactory ou d’un AuthenticationContext ? En d’autres termes, pourquoi l’application devrait-elle savoir quoi que ce soit en raison de problèmes de câblage posés par certains problèmes de conception dans ASP.NET Identity?

    Au lieu de cela, en ajustant l’enregistrement pour IAuthenticationManager nous pouvons renvoyer un composant d’authentification à partir d’un OwinContext nouvellement créé au démarrage / heure de vérification et renvoyer AuthenticationManager ‘normal’ ou configuré au moment de l’exécution. Cela supprime le besoin d’une usine et déplace la responsabilité vers la racine de composition où elle devrait être. Et vous permet d’injecter IAuthenticationManager partout où vous en avez besoin, tout en permettant d’appeler .Verify() .

    Le code ressemble à:

     container.RegisterPerWebRequest(() => AdvancedExtensions.IsVerifying(container) ? new OwinContext(new Dictionary()).Authentication : HttpContext.Current.GetOwinContext().Authentication); 

    Une solution encore plus SOLIDE consisterait toutefois à ne pas dépendre du tout de IAuthenticationManager , car, selon cette interface, nous enfreignons le principe de séparation des interfaces, rendant difficile la création d’une implémentation de proxy retardant la création.

    Vous pouvez le faire en définissant une abstraction qui correspond à vos besoins et uniquement à vos besoins. En IAuthenticationManager les appels de modèle d’identité à IAuthenticationManager cette abstraction n’aurait besoin de rien de plus que les .SignIn() et .SignOut() . Toutefois, cela vous obligerait à refactoriser complètement le compte AccountController de merde que vous avez “gratuitement” par le modèle Visual Studio, ce qui peut être une entreprise.

    Voir ma réponse ici .

    Bien que vous accédiez à un type différent, le problème est le même. Vous ne pouvez pas compter sur les propriétés de HttpContext lors du démarrage de l’application, car l’application est initialisée en dehors du contexte de l’utilisateur. La solution consiste à créer une fabrique abstraite pour lire les valeurs au moment de l’exécution plutôt qu’à la création de l’object et injecter la fabrique plutôt que le type IAuthenticationManager dans votre contrôleur.

     public class AccountController { private readonly AngularAppUserManager _userManager; private readonly AngularAppSignInManager _signInManager; private readonly IAuthenticationManagerFactory _authenticationManagerFactory; public AccountController(AngularAppUserManager userManager , AngularAppSignInManager signinManager , IAuthenticationManagerFactory authenticationManagerFactory) { this._userManager = userManager; this._signInManager = signinManager; this._authenticationManagerFactory = authenticationManagerFactory; } private IAuthenticationManager AuthenticationManager { get { return this._authenticationManagerFactory.Create(); } } private void DoSomething() { // Now it is safe to call into HTTP context var manager = this.AuthenticationManger; } } public interface IAuthenticationMangerFactory { IAuthenticationManger Create(); } public class AuthenticationMangerFactory { public IAuthenticationManger Create() { HttpContext.Current.GetOwinContext().Authentication; } } // And register your factory... container.Register();