web-dev-qa-db-fra.com

Comment brancher mon conteneur Autofac sur ASP. NET Identity 2.1

J'ai étudié les nouvelles fonctionnalités de la nouvelle version d'ASP.NET Identity 2.1 et l'une de ses améliorations réside dans les nouvelles fonctionnalités IoC intégrées au middleware OWIN. L'une des phrases que j'ai regardées dans les exemples est celle-ci:

app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

Cette phrase reçoit un délégué de fonction qui retourne une nouvelle instance d'une implémentation de gestionnaire fournie dans les exemples:

 public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options,
        IOwinContext context)
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>())); 

Personnellement, je n'aime pas cette implémentation car je ne suis pas en mesure d'utiliser un conteneur pour injecter la dépendance que je souhaite pour ces gestionnaires.

Il y a aussi un "IdentityFactoryOptions" et un "IOwinContext" qui sont "magiquement" injectés dans la fonction que je ne peux pas extraire dans mon conteneur IoC.

Quelqu'un at-il une meilleure solution de contournement sur cette implémentation?

38
renelopez

Je pars d'une installation MVC5 prête à l'emploi et j'utilise AutoFac comme conteneur IoC. Il semble que j'essaie d'atteindre un objectif similaire à vous, alors laissez-moi vous expliquer ce que j'ai fait. En tant que clause de non-responsabilité, je suis relativement nouveau dans l'utilisation de l'IoC et de l'identité.

Je crois que l'IOwinContext n'est pas nécessaire dans un rôle d'IoC si vous utilisez le vôtre - je suis passé à l'enregistrement de mon ApplicationUserManager avec AutoFac. Pour y parvenir, j'ai dû:

Supprimez les lignes CreatePerOwinContext de Startup.Auth car je vais enregistrer ApplicationDbContext et ApplicationUserManager dans AutoFac.

//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

Modifiez les arguments du constructeur ApplicationUserManager et incluez tout dans la fonction Create.

public ApplicationUserManager(IUserStore<ApplicationUser> store, IdentityFactoryOptions<ApplicationUserManager> options)
        : base(store)
{
    //all the code from the 'Create' function here, using `this` for `manager`
}

Définissez le AccountController pour avoir un seul constructeur prenant un ApplicationUserManager comme argument et supprimant la propriété UserManager qui récupère le ApplicationUserManager du OwinContext.

private ApplicationUserManager _userManager; //every thing that needs the old UserManager property references this now
public AccountController(ApplicationUserManager userManager) 
{
    _userManager = userManager;
}

Enregistrez tout avec AutoFac, y compris une instance d'IdentityFactoryOptions.

var x = new ApplicationDbContext();
builder.Register<ApplicationDbContext>(c => x);
builder.Register<UserStore<ApplicationUser>>(c => new UserStore<ApplicationUser>(x)).AsImplementedInterfaces();
builder.Register<IdentityFactoryOptions<ApplicationUserManager>>(c => new IdentityFactoryOptions<ApplicationUserManager>()
{
    DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName")
});
builder.RegisterType<ApplicationUserManager>();

Voilà le résumé sommaire. J'ai peut-être manqué quelques autres ajustements que j'ai dû faire en cours de route.

48
Ben Bartle

La réponse de Ben donne une bonne idée générale, mais elle instancie manuellement le DbContext et utilise cette instance lors de l'enregistrement du reste des types. IMO, c'est une mauvaise idée (il ne faut pas utiliser le même contexte db éternel pour TOUTES les requêtes).

Le commentaire de Derek est une grande amélioration, mais il ne transmet pas le contexte de la base de données au magasin d'utilisateurs, ce qui entraîne des erreurs telles que "Le type d'entité ApplicationUser ne fait pas partie du modèle pour le contexte actuel.".

J'ai inclus mon code ci-dessous, pour référence - il est vraiment similaire à celui de Derek.

builder.RegisterType<MyApplicationContext>().AsSelf().InstancePerRequest()
//...

builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationSignInManager>().AsSelf().InstancePerRequest();
builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<MyApplicationContext>())).AsImplementedInterfaces().InstancePerRequest();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();
builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager>
{
    DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("Application​")
}); 
29
Vlad Iliescu

Pour référence, voici comment vous pouvez tout câbler avec Unity:

var container = new UnityContainer();

container.RegisterType<MyDbContext>(new InjectionConstructor("ConnectionStringName"));

container.RegisterType<IAuthenticationManager>(
   new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));

container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
   new InjectionConstructor(typeof(MyDbContext)));

container.RegisterType<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>(
   new InjectionConstructor(typeof(MyDbContext)));

container.RegisterType<IdentityFactoryOptions<ApplicationUserManager>>(new InjectionFactory(x =>
   new IdentityFactoryOptions<ApplicationUserManager>
   {
      DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName")
   }));

container.RegisterType<ApplicationSignInManager>();

DependencyResolver.SetResolver(new UnityDependencyResolver(container));
4

Maintenant détaillé pour intégration MVC5 Owin sur Autofac Docs :

"

  • Faites tout le nécessaire pour l'intégration MVC standard - enregistrez les contrôleurs, définissez le résolveur de dépendances, etc.
  • Configurez votre application avec l'intégration de base Autofac OWIN.
  • Ajoutez une référence au package Autofac.Mvc5.Owin NuGet.
  • Dans votre classe de démarrage d'application, enregistrez le middleware Autofac MVC après avoir enregistré le middleware de base Autofac.

    public class Startup
    {
      public void Configuration(IAppBuilder app)
      {
       var builder = new ContainerBuilder();
    
      // STANDARD MVC SETUP:
    
      // Register your MVC controllers.
      builder.RegisterControllers(typeof(MvcApplication).Assembly);
    
      // Run other optional steps, like registering model binders,
      // web abstractions, etc., then set the dependency resolver
      // to be Autofac.
      var container = builder.Build();
      DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    
      // OWIN MVC SETUP:
    
      // Register the Autofac middleware FIRST, then the Autofac MVC middleware.
      app.UseAutofacMiddleware(container);
      app.UseAutofacMvc();
      }
    }
    

    "

J'ai également le wrapper RoleManager ajouté:

builder.RegisterType<RoleStore<IdentityRole>>().As<IRoleStore<IdentityRole, string>>();

selon réponse SO

2
OzBob

J'ai géré la solution de contournement en utilisant le localisateur de services autofac:

app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());

Oui, ce n'est pas suffisant, mais en attendant, nous pourrions utiliser la même portée d'objet que celle déclarée dans le processus d'enregistrement autofac.

1
atthakorn