web-dev-qa-db-fra.com

Quel est le but de la méthode d'extension CreatePerOwinContext dans l'implémentation OWIN par Microsoft

Je suis un débutant en ASP.NET et j'apprends actuellement ASP.NET Identity. Je sais qu'il est construit sur l'implémentation OWIN par Microsoft, et j'apprends également cela aussi. Donc, je suis tombé sur la méthode d'extension CreatePerOwinContext dans le code de démarrage Owin, et je ne vois pas de raison claire de l'utiliser. Est-ce une sorte de conteneur d'injection de dépendance? Quel est le véritable objectif de la méthode? Dans quel cas devrait-il être appliqué?

48
Next Developer

CreatePerOwinContext enregistre un rappel statique que votre application utilisera pour récupérer une nouvelle instance d'un type spécifié.
Ce rappel sera appelé une fois par demande et stockera l'objet/les objets dans OwinContext afin que vous puissiez les utiliser tout au long de L'application.

Disons que vous avez défini votre propre implémentation de IdentityDbContext :

public class ApplicationDatabaseContext : IdentityDbContext<MyApplicationUser, MyRole, Guid, MyUserLogin, MyUserRole, MyUserClaim>
{
    public ApplicationDatabaseContext() : base("<connection string>")
    {
    }

    public static ApplicationDatabaseContext Create()
    {
        return new ApplicationDatabaseContext();
    }

        protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
        {
        base.OnModelCreating(modelBuilder);

        // Customize your table creation here.

            #region USERS - INFOS

        modelBuilder.Entity<UserInfo>()
            .Property(p => p.FirstName)
            .HasColumnType("varchar")
            .HasMaxLength(70);

        modelBuilder.Entity<UserInfo>()
            .Property(p => p.LastName)
            .HasColumnType("varchar")
            .HasMaxLength(70);

        modelBuilder.Entity<UserInfo>()
            .Property(p => p.Address)
            .HasColumnType("varchar")
            .HasMaxLength(100);

        modelBuilder.Entity<UserInfo>()
            .Property(p => p.City)
            .HasColumnType("varchar")
            .HasMaxLength(100);

        modelBuilder.Entity<UserInfo>()
            .ToTable("UsersInfo");

        #endregion  
        }

        public DbSet<UserInfo> UsersInfo { get; set; }
}

et votre implémentation de serManager :

public class ApplicationUserManager : UserManager<MyApplicationUser, Guid>
{
    public ApplicationUserManager(IUserStore<MyApplicationUser, Guid> store) : base(store)
        {
        }

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

            manager.UserValidator = new UserValidator<MyApplicationUser, Guid>(manager)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };

            manager.PasswordValidator = new PasswordValidator()
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = false,    
                // RequireDigit = true,
                RequireLowercase = false,
                RequireUppercase = false,
            };

            var dataProtectionProvider = options.DataProtectionProvider;

            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider = new DataProtectorTokenProvider<MyApplicationUser, Guid>(dataProtectionProvider.Create("PasswordReset"));
            }

            return (manager);
        }
}

Dans votre démarrage Owin , vous enregistrerez le rappel:

// IAppBuilder app

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

qui appellera la méthode statique:

public static ApplicationDatabaseContext Create()
{
    return new ApplicationDatabaseContext();
}

et

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
    ...
}

Vous pourrez désormais accéder à votre contexte de base de données et à votre gestionnaire d'utilisateurs de manière simple et directe:

ApplicationDatabaseContext dbContext = context.OwinContext.Get<ApplicationDatabaseContext>();
ApplicationUserManager userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

Dans votre ApiController (si vous utilisez WebApi):

IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
ApplicationUserManager applicationUserManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
65
LeftyX

Quel est le véritable objectif de la méthode? Dans quel cas faut-il l'appliquer?

Pour répondre plus directement à votre question, cela ne sert à rien.

  1. C'est une sorte d'usine IoC, que certaines personnes aiment utiliser.
  2. Celui-ci vous fait utiliser le leur (IoC) sur votre choix.
  3. (Je n'aime pas l'IoC, cela ressemble à un anti-modèle pour les personnes qui veulent se sentir chaudes et floues et utiliser le terme "architecture".)
  4. Mais sérieusement, ce modèle n'interfère pas avec l'IoC, il fonctionne en usine statique IoC! Qui était cette idée? Pourquoi ne pas utiliser vous-même la fonction Factory? Vous devez maintenant vous rappeler (Google) d'un appel d'API supplémentaire, et lorsque vous appuyez sur F12 sur Get, cela ne vous aide nulle part.

Que devez-vous faire à la place alors?

Personnellement, je suis un fan de l'utilisation de OO pour cela, rappelez-vous OO? La ferme Pepperidge se souvient. Avec OO, vous gardez le contrôle, vous pouvez déboguer, vous connecter et vous pouvez étendre.

public class BaseApiController : ApiController
{
    private AppDbContext _db = null;

    protected AppDbContext db
    {
        get
        {
            if (_db == null)
            {
                _db = AppDbContext.Create(); //Hey look a proper factory that you can extend with other overloads! And I can debug this line - neat!
            }
            return _db;
        }

    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_db != null)
                _db.Dispose();
        }
    }

}

Tout cela pourrait être une perte de temps, si quelqu'un trouve de la documentation sur les raisons pour lesquelles les ingénieurs de Microsoft ont mis cela, ils pourraient avoir une bonne raison, mais j'en doute, alors revotons cette réponse en attendant.

MISE À JOUR 1

Voici pourquoi, pourquoi il est là pour Microsoft: https://blogs.msdn.Microsoft.com/webdev/2014/02/12/per-request-lifetime-management-for-usermanager-class-in- asp-net-identity /

Fondamentalement, le UserManager et tous les autres sont conçus pour ce type de structure. Les contrôles de sécurité ont lieu dans le pipeline, alors pourquoi ne pas avoir un singleton lié à la demande, pour réduire les déchets? Parce que c'est caché.

Je recommanderais toujours de créer votre propre instance du contexte db sur une classe de base, cela la rend beaucoup plus propre à utiliser. Si vous le voulez vraiment, vous pouvez avoir une propriété dans votre classe de base qui récupère le singleton de OwinContext.

Combien de temps perdons-nous à essayer de travailler sur ces API fantastiques et à autoriser des attributs et autres, alors que tout ce que nous voulons faire est de:

public void DoSomething()
{
   DemandAuthenticated();
   DemandAuthorised(typeof(somethingClass), "DoSomething");
}

De toute évidence, je préfère le code détaillé que vous pouvez voir.

Mise à jour 2

Les contextes EF ne doivent pas être considérés comme des singletons, et certainement pas via un modèle IoC ou de référentiel.

Généralement, oui l'IoC peut être bon dans des situations. Mais spécifiquement pour un dbContext? Non.

1) Les contextes EF DB sont une unité de travail, ils doivent être de courte durée. Si vous les laissez fonctionner pendant longtemps, le cache d'objets ralentira les requêtes et les mises à jour/insertions dans la base de données sous-jacente ralentiront. Il est conçu pour avoir une courte durée de vie. 2) De plus, les contextes EF sont déjà faiblement couplés. Vous pouvez modifier le SGBDR derrière un contexte dans la chaîne de connexion, vous pouvez même utiliser uniquement de la mémoire. 3) EF a LINQ qui est très flexible, expressif et sûr pour le type. 4) La base de données n'est pas un service de niveau métier pour IoC, c'est un outil que les services utilisent pour communiquer avec la base de données. Peut-être que vous pourriez avoir une sorte de service IEmail accessible via IoC. Mais il doit accéder à la base de données interne à l'aide d'un nouveau contexte EF qui est supprimé rapidement après l'achèvement des requêtes. 5) Étant donné 1-4 ci-dessus, nous ne voulons certainement pas que des couches d'interface intermédiaires (service ou référentiel) gâchent tous les avantages de l'utilisation d'EF en premier lieu.

4
Todd

vous pouvez utiliser typeof pour obtenir le nom comme ceci:

HttpContext.GetOwinContext().Get<ApplicationDbContext>(typeof(ApplicationDbContext).ToString());
2
charlie Francis