Migration des données du fournisseur universel pour l’appartenance et les profils utilisateur vers ASP.NET Identity (C#)

par Pranav Rastogi, Rick Anderson, Robert McMurray, Suhas Joshi

Ce tutoriel décrit les étapes nécessaires à la migration des données utilisateur et de rôle, ainsi que des données de profil utilisateur créées à l’aide de Fournisseurs universels d’une application existante vers le modèle d’identité ASP.NET. L’approche mentionnée ici pour migrer les données de profil utilisateur peut également être utilisée dans une application avec une appartenance SQL.

Avec la publication de Visual Studio 2013, l’équipe ASP.NET a introduit un nouveau système d’identité ASP.NET, et vous pouvez en savoir plus sur cette version ici. Suite à l’article relatif à la migration d’applications web de l’appartenance SQL vers le nouveau système d’identité, cet article illustre les étapes de migration des applications existantes qui suivent le modèle Fournisseurs pour la gestion des utilisateurs et des rôles vers le nouveau modèle d’identité. L’objectif de ce didacticiel est principalement de migrer les données de profil utilisateur pour les raccorder en toute transparence au nouveau système. La migration des informations sur l’utilisateur et le rôle est similaire pour l’appartenance SQL. L’approche suivie pour migrer les données de profil peut également être utilisée dans une application avec l’appartenance SQL.

Par exemple, nous allons commencer par une application web créée à l’aide de Visual Studio 2012 qui utilise le modèle Fournisseurs. Nous allons ensuite ajouter du code pour la gestion des profils, inscrire un utilisateur, ajouter des données de profil pour les utilisateurs, migrer le schéma de base de données, puis modifier l’application pour utiliser le système d’identité pour la gestion des utilisateurs et des rôles. À titre de test de migration, les utilisateurs créés à l’aide de Fournisseurs universels doivent pouvoir se connecter et les nouveaux utilisateurs doivent pouvoir s’inscrire.

Notes

Vous trouverez l’exemple complet à l’adresse https://github.com/suhasj/UniversalProviders-Identity-Migrations.

Résumé de la migration des données de profil

Avant de commencer les migrations, examinons l’expérience de stockage des données de profil dans le modèle Fournisseurs. Les données de profil pour les utilisateurs de l’application peuvent être stockées de plusieurs façons, la plus courante d’entre elles étant l’utilisation des fournisseurs de profil intégré fournis avec le Fournisseurs universels. Les étapes comprendraient

  1. Ajoutez une classe qui a des propriétés utilisées pour stocker les données de profil.
  2. Ajoutez une classe qui étend « ProfileBase » et implémente des méthodes pour obtenir les données de profil ci-dessus pour l’utilisateur.
  3. Activez l’utilisation des fournisseurs de profil par défaut dans le fichier web.config et définissez la classe déclarée à l’étape 2 à utiliser pour accéder aux informations de profil.

Les informations de profil sont stockées sous forme de données xml et binaires sérialisées dans la table « Profils » de la base de données.

Après avoir migré l’application pour utiliser le nouveau système d’identité ASP.NET, les informations de profil sont désérialisées et stockées en tant que propriétés sur la classe utilisateur. Chaque propriété peut ensuite être mappée aux colonnes de la table utilisateur. L’avantage ici est que les propriétés peuvent être travaillées directement à l’aide de la classe user en plus de ne pas avoir à sérialiser/désérialiser les informations de données chaque fois lors de l’accès à celle-ci.

Mise en route

  1. Créez une application ASP.NET 4.5 Web Forms dans Visual Studio 2012. L’exemple actuel utilise le modèle Web Forms, mais vous pouvez également utiliser l’application MVC.

    Capture d’écran d’une application Web Forms nouvellement créée dans Visual Studio 2012 à l’aide du modèle Web Forms.

  2. Créer un dossier « Modèles » pour stocker les informations de profil

    Capture d’écran du nouveau dossier appelé Modèles créé pour stocker les informations de profil.

  3. Par exemple, stockons la date de naissance, la ville, la taille et le poids de l’utilisateur dans le profil. La hauteur et le poids sont stockés sous la forme d’une classe personnalisée appelée « PersonalStats ». Pour stocker et récupérer le profil, nous avons besoin d’une classe qui étend « ProfileBase ». Nous allons créer une classe « AppProfile » pour obtenir et stocker des informations de profil.

    public class ProfileInfo
    {
        public ProfileInfo()
        {
            UserStats = new PersonalStats();
        }
        public DateTime? DateOfBirth { get; set; }
        public PersonalStats UserStats { get; set; }
        public string City { get; set; }
    }
    
    public class PersonalStats
    {
        public int? Weight { get; set; }
        public int? Height { get; set; }
    }
    
    public class AppProfile : ProfileBase
    {
        public ProfileInfo ProfileInfo
        {
            get { return (ProfileInfo)GetPropertyValue("ProfileInfo"); }
        }
        public static AppProfile GetProfile()
        {
            return (AppProfile)HttpContext.Current.Profile;
        }
        public static AppProfile GetProfile(string userName)
        {
            return (AppProfile)Create(userName);
        }
    }
    
  4. Activez le profil dans le fichier web.config . Entrez le nom de la classe à utiliser pour stocker/récupérer les informations utilisateur créées à l’étape 3.

    <profile defaultProvider="DefaultProfileProvider" enabled="true"
        inherits="UniversalProviders_ProfileMigrations.Models.AppProfile">
      <providers>
        .....
      </providers>
    </profile>
    
  5. Ajoutez une page de formulaires web dans le dossier « Compte » pour obtenir les données de profil de l’utilisateur et les stocker. Cliquez avec le bouton droit sur le projet et sélectionnez « Ajouter un nouvel élément ». Ajoutez une nouvelle page webforms avec master page « AddProfileData.aspx ». Copiez ce qui suit dans la section « MainContent » :

    <h2> Add Profile Data for <%# User.Identity.Name %></h2>
    <asp:Label Text="" ID="Result" runat="server" />
    <div>
        Date of Birth:
        <asp:TextBox runat="server" ID="DateOfBirth"/>
    </div>
    <div>
        Weight:
        <asp:TextBox runat="server" ID="Weight"/>
    </div>
    <div>
        Height:
        <asp:TextBox runat="server" ID="Height"/>
    </div>
    <div>
        City:
        <asp:TextBox runat="server" ID="City"/>
    </div>
    <div>
        <asp:Button Text="Add Profile" ID="Add" OnClick="Add_Click" runat="server" />
    </div>
    

    Ajoutez le code suivant dans le code behind :

    protected void Add_Click(object sender, EventArgs e)
    {
        AppProfile profile = AppProfile.GetProfile(User.Identity.Name);
        profile.ProfileInfo.DateOfBirth = DateTime.Parse(DateOfBirth.Text);
        profile.ProfileInfo.UserStats.Weight = Int32.Parse(Weight.Text);
        profile.ProfileInfo.UserStats.Height = Int32.Parse(Height.Text);
        profile.ProfileInfo.City = City.Text;
        profile.Save();
    }
    

    Ajoutez l’espace de noms sous lequel la classe AppProfile est définie pour supprimer les erreurs de compilation.

  6. Exécutez l’application et créez un utilisateur avec le nom d’utilisateur « olduser ». Accédez à la page « AddProfileData » et ajoutez des informations de profil pour l’utilisateur.

    Capture d’écran de la page Ajouter des données de profil pour ajouter des informations de profil pour l’utilisateur.

Vous pouvez vérifier que les données sont stockées en tant que xml sérialisé dans la table « Profils » à l’aide de la fenêtre Server Explorer. Dans Visual Studio, dans le menu « Affichage », choisissez « Serveur Explorer ». Il doit y avoir une connexion de données pour la base de données définie dans le fichier web.config . Le fait de cliquer sur la connexion de données affiche différentes sous-catégories. Développez « Tables » pour afficher les différentes tables de votre base de données, puis cliquez avec le bouton droit sur « Profils » et choisissez « Afficher les données de table » pour afficher les données de profil stockées dans la table Profils.

Capture d’écran de la fenêtre Server Explorer montrant les données stockées dans la table « Profiles ».

Capture d’écran de la table de données Profils.

Migration du schéma de base de données

Pour que la base de données existante fonctionne avec le système d’identité, nous devons mettre à jour le schéma dans la base de données Identity pour prendre en charge les champs que nous avons ajoutés à la base de données d’origine. Cette opération peut être effectuée à l’aide de scripts SQL pour créer de nouvelles tables et copier les informations existantes. Dans la fenêtre « Server Explorer », développez « DefaultConnection » pour afficher les tables. Cliquez avec le bouton droit sur Tables et sélectionnez « Nouvelle requête »

Capture d’écran de la mise à jour du schéma dans la base de données d’identité en sélectionnant Nouvelle requête.

Collez le script SQL à partir de https://raw.github.com/suhasj/UniversalProviders-Identity-Migrations/master/Migration.txt et exécutez-le. Si la valeur « DefaultConnection » est actualisée, nous pouvons voir que les nouvelles tables sont ajoutées. Vous pouvez case activée les données dans les tables pour voir que les informations ont été migrées.

Capture d’écran de la connexion par défaut actualisée et de nouvelles tables ajoutées.

Migration de l’application pour utiliser ASP.NET Identity

  1. Installez les packages Nuget nécessaires pour ASP.NET Identity :

    • Microsoft.AspNet.Identity.EntityFramework
    • Microsoft.AspNet.Identity.Owin
    • Microsoft.Owin.Host.SystemWeb
    • Microsoft.Owin.Security.Facebook
    • Microsoft.Owin.Security.Google
    • Microsoft.Owin.Security.MicrosoftAccount
    • Microsoft.Owin.Security.Twitter

    Vous trouverez plus d’informations sur la gestion des packages Nuget ici.

  2. Pour utiliser des données existantes dans la table, nous devons créer des classes de modèle qui sont mappées aux tables et les raccordent au système d’identité. Dans le cadre du contrat Identity, les classes de modèle doivent soit implémenter les interfaces définies dans la dll Identity.Core, soit étendre l’implémentation existante de ces interfaces disponibles dans Microsoft.AspNet.Identity.EntityFramework. Nous allons utiliser les classes existantes pour les rôles, les connexions d’utilisateurs et les revendications utilisateur. Nous devons utiliser un utilisateur personnalisé pour notre exemple. Cliquez avec le bouton droit sur le projet et créez le dossier « IdentityModels ». Ajoutez une nouvelle classe « User » comme indiqué ci-dessous :

    using Microsoft.AspNet.Identity.EntityFramework;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using UniversalProviders_ProfileMigrations.Models;
    
    namespace UniversalProviders_Identity_Migrations
    {
        public class User : IdentityUser
        {
            public User()
            {
                CreateDate = DateTime.UtcNow;
                IsApproved = false;
                LastLoginDate = DateTime.UtcNow;
                LastActivityDate = DateTime.UtcNow;
                LastPasswordChangedDate = DateTime.UtcNow;
                Profile = new ProfileInfo();
            }
    
            public System.Guid ApplicationId { get; set; }
            public bool IsAnonymous { get; set; }
            public System.DateTime? LastActivityDate { get; set; }
            public string Email { get; set; }
            public string PasswordQuestion { get; set; }
            public string PasswordAnswer { get; set; }
            public bool IsApproved { get; set; }
            public bool IsLockedOut { get; set; }
            public System.DateTime? CreateDate { get; set; }
            public System.DateTime? LastLoginDate { get; set; }
            public System.DateTime? LastPasswordChangedDate { get; set; }
            public System.DateTime? LastLockoutDate { get; set; }
            public int FailedPasswordAttemptCount { get; set; }
            public System.DateTime? FailedPasswordAttemptWindowStart { get; set; }
            public int FailedPasswordAnswerAttemptCount { get; set; }
            public System.DateTime? FailedPasswordAnswerAttemptWindowStart { get; set; }
            public string Comment { get; set; }
            public ProfileInfo Profile { get; set; }
        }
    }
    

    Notez que « ProfileInfo » est désormais une propriété sur la classe utilisateur. Par conséquent, nous pouvons utiliser la classe utilisateur pour travailler directement avec les données de profil.

Copiez les fichiers dans les dossiers IdentityModels et IdentityAccount à partir de la source de téléchargement ( https://github.com/suhasj/UniversalProviders-Identity-Migrations/tree/master/UniversalProviders-Identity-Migrations ). Ceux-ci ont les classes de modèle restantes et les nouvelles pages nécessaires pour la gestion des utilisateurs et des rôles à l’aide des API d’identité ASP.NET. L’approche utilisée est similaire à l’appartenance SQL et l’explication détaillée est disponible ici.

Certaines commandes ne sont pas prises en charge si l’application utilise SQLite comme magasin de données Identity. En raison des limitations du moteur de base de données, Alter les commandes lèvent l’exception suivante :

« System.NotSupportedException : SQLite ne prend pas en charge cette opération de migration. »

Pour contourner ce problème, exécutez des migrations Code First sur la base de données pour modifier les tables.

Copie des données de profil dans les nouvelles tables

Comme mentionné précédemment, nous devons désérialiser les données xml dans les tables Profiles et les stocker dans les colonnes de la table AspNetUsers. Les nouvelles colonnes ont été créées dans la table users à l’étape précédente. Il ne reste donc plus qu’à remplir ces colonnes avec les données nécessaires. Pour ce faire, nous allons utiliser une application console qui est exécutée une fois pour remplir les colonnes nouvellement créées dans la table users.

  1. Créez une application console dans la solution sortante.

    Capture d’écran de la création d’une application console dans la solution sortante.

  2. Installez la dernière version du package Entity Framework.

  3. Ajoutez l’application web créée ci-dessus en tant que référence à l’application console. Pour ce faire, cliquez avec le bouton droit sur Projet, puis sur « Ajouter des références », puis sur Solution, cliquez sur le projet et cliquez sur OK.

  4. Copiez le code ci-dessous dans la classe Program.cs. Cette logique lit les données de profil pour chaque utilisateur, les sérialise en tant qu’objet « ProfileInfo » et les stocke dans la base de données.

    public class Program
    {
        var dbContext = new ApplicationDbContext();
        foreach (var profile in dbContext.Profiles)
        {
            var stringId = profile.UserId.ToString();
            var user = dbContext.Users.Where(x => x.Id == stringId).FirstOrDefault();
            Console.WriteLine("Adding Profile for user:" + user.UserName);
            var serializer = new XmlSerializer(typeof(ProfileInfo));
            var stringReader = new StringReader(profile.PropertyValueStrings);
            var profileData = serializer.Deserialize(stringReader) as ProfileInfo;
            if (profileData == null)
            {
                Console.WriteLine("Profile data deserialization error for user:" + user.UserName);
            }
            else
            {
                user.Profile = profileData;
            }
        }
        dbContext.SaveChanges();
    }
    

    Certains des modèles utilisés sont définis dans le dossier « IdentityModels » du projet d’application web. Vous devez donc inclure les espaces de noms correspondants.

  5. Le code ci-dessus fonctionne sur le fichier de base de données dans le dossier App_Data du projet d’application web créé aux étapes précédentes. Pour y faire référence, mettez à jour la chaîne de connexion dans le fichier app.config de l’application console avec la chaîne de connexion dans le web.config de l’application web. Fournissez également le chemin d’accès physique complet dans la propriété « AttachDbFilename ».

  6. Ouvrez une invite de commandes et accédez au dossier bin de l’application console ci-dessus. Exécutez l’exécutable et passez en revue la sortie du journal, comme illustré dans l’image suivante.

    Capture d’écran de l’exécutable dans l’invite de commandes pour exécuter et passer en revue la sortie du journal.

  7. Ouvrez la table « AspNetUsers » dans le serveur Explorer et vérifiez les données dans les nouvelles colonnes qui contiennent les propriétés. Ils doivent être mis à jour avec les valeurs de propriété correspondantes.

Vérifier les fonctionnalités

Utilisez les pages d’appartenance nouvellement ajoutées qui sont implémentées à l’aide de ASP.NET Identity pour connecter un utilisateur à partir de l’ancienne base de données. L’utilisateur doit pouvoir se connecter à l’aide des mêmes informations d’identification. Essayez les autres fonctionnalités telles que l’ajout d’OAuth, la création d’un utilisateur, la modification d’un mot de passe, l’ajout de rôles, l’ajout d’utilisateurs à des rôles, etc.

Les données de profil de l’ancien utilisateur et des nouveaux utilisateurs doivent être récupérées et stockées dans la table users. L’ancienne table ne doit plus être référencée.

Conclusion

L’article décrit le processus de migration des applications web qui utilisent le modèle de fournisseur pour l’appartenance à ASP.NET Identity. L’article décrit également la migration des données de profil pour que les utilisateurs soient connectés au système d’identité. Laissez les commentaires ci-dessous pour les questions et les problèmes rencontrés lors de la migration de votre application.

Merci à Rick Anderson et Robert McMurray d’avoir consulté l’article.