Compartir a través de


Migrar datos de proveedor universal para la pertenencia y los perfiles de usuario a ASP.NET Identity (C#)

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

En este tutorial se describen los pasos necesarios para migrar los datos de usuario y rol y los datos de perfil de usuario creados mediante proveedores universales de una aplicación existente al modelo de identidad de ASP.NET. El enfoque mencionado aquí para migrar datos de perfil de usuario también se puede usar en una aplicación con pertenencia a SQL.

Con la versión de Visual Studio 2013, el equipo de ASP.NET introdujo un nuevo sistema ASP.NET Identity y puede obtener más información sobre esa versión aquí. Siguiendo el artículo para migrar aplicaciones web de la pertenencia SQL al nuevo sistema de identidades, en este artículo se muestran los pasos para migrar las aplicaciones existentes que siguen el modelo de proveedores para la administración de roles y usuarios al nuevo modelo de identidad. El enfoque de este tutorial se centrará principalmente en migrar los datos del perfil de usuario para enlazarlos sin problemas al nuevo sistema. La migración de información de usuario y rol es similar a la pertenencia a SQL. El enfoque seguido para migrar los datos de perfil también se puede usar en una aplicación con pertenencia a SQL.

Por ejemplo, empezaremos con una aplicación web creada con Visual Studio 2012 que usa el modelo de proveedores. A continuación, agregaremos código para la administración de perfiles, registraremos un usuario, agregaremos datos de perfil para los usuarios, migraremos el esquema de la base de datos y luego, cambiaremos la aplicación para usar el sistema de identidades para la administración de roles y usuarios. Como prueba de la migración, los usuarios creados con proveedores universales deberían poder iniciar sesión y los nuevos usuarios deberían poder registrarse.

Nota:

Puede encontrar un ejemplo completo en https://github.com/suhasj/UniversalProviders-Identity-Migrations.

Resumen de la migración de datos de perfil

Antes de empezar con las migraciones, echemos un vistazo a la experiencia de almacenar datos de perfil en el modelo de proveedores. Los datos de perfil de los usuarios de la aplicación pueden almacenarse de varias formas, siendo la más habitual el uso de los proveedores de perfiles integrados que se suministran junto con los proveedores universales. Los pasos incluirían

  1. Agregue una clase que tenga propiedades usadas para almacenar datos de perfil.
  2. Agregue una clase que extienda "ProfileBase" e implemente métodos para obtener los datos de perfil anteriores para el usuario.
  3. Habilite el uso de proveedores de perfiles predeterminados en el archivo web.config y defina la clase declarada en el paso 2 que se usará para acceder a la información del perfil.

La información del perfil se almacena como datos xml serializados y binarios en la tabla "Perfiles" de la base de datos.

Después de migrar la aplicación para usar el nuevo sistema de identidad de ASP.NET, la información del perfil se deserializa y se almacena como propiedades en la clase de usuario. Después, cada propiedad se puede asignar a columnas de la tabla de usuario. La ventaja aquí es que se puede trabajar directamente con las propiedades utilizando la clase de usuario además de no tener que serializar/deserializar la información de los datos cada vez que se accede a ella.

Introducción

  1. Cree una aplicación de formularios ASP.NET 4.5 Web Forms en Visual Studio 2012. En el ejemplo actual se usa la plantilla de Formularios Web Forms, pero también puede usar la aplicación MVC.

    Screenshot of a newly created Web Forms application in Visual Studio 2012 using the Web Forms template.

  2. Crear una nueva carpeta "Models" para almacenar la información del perfil

    Screenshot of the new folder called Models created to store profile information.

  3. Por ejemplo, vamos a almacenar la fecha de nacimiento, ciudad, altura y peso del usuario en el perfil. La altura y el peso se almacenan como una clase personalizada llamada "PersonalStats". Para almacenar y recuperar el perfil, necesitamos una clase que extienda "ProfileBase". Vamos a crear una nueva clase "AppProfile" para obtener y almacenar información de perfil.

    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. Habilite el perfil en el archivo web.config. Escriba el nombre de clase que se usará para almacenar o recuperar la información de usuario creada en el paso 3.

    <profile defaultProvider="DefaultProfileProvider" enabled="true"
        inherits="UniversalProviders_ProfileMigrations.Models.AppProfile">
      <providers>
        .....
      </providers>
    </profile>
    
  5. Agregue una página de formularios web en la carpeta "Account" para obtener los datos del perfil del usuario y almacenarlos. Haga clic con el botón derecho en el proyecto y seleccione "Agregar nuevo elemento". Agregue una nueva página de formularios web con la página maestra "AddProfileData.aspx". Copie lo siguiente en la sección "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>
    

    Agregue el código siguiente en el código subyacente:

    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();
    }
    

    Agregue el espacio de nombres en el que se define la clase AppProfile para quitar los errores de compilación.

  6. Ejecute la aplicación y cree un nuevo usuario con el nombre de usuario "olduser". Vaya a la página "AddProfileData" y agregue información de perfil para el usuario.

    Screenshot of the Add Profile Data page to add profile information for the user.

Puede comprobar que los datos se almacenan como xml serializados en la tabla "Perfiles" mediante la ventana Explorador de servidores. En Visual Studio, en el menú "Ver", elija "Explorador de servidores". Debe haber una conexión de datos para la base de datos definida en el archivo web.config. Al hacer clic en la conexión de datos se muestran diferentes sub categorías. Expanda "Tablas" para mostrar las distintas tablas de la base de datos y haga clic con el botón derecho en "Perfiles" y elija "Mostrar datos de tabla" para ver los datos almacenados del perfil en la tabla Perfiles.

Screenshot of the Server Explorer window that shows the data stored in the 'Profiles' table.

Screenshot of the Profiles data table.

Migración del esquema de base de datos

Para que la base de datos existente funcione con el sistema identity, es necesario actualizar el esquema de la base de datos identity para admitir los campos que hemos agregado a la base de datos original. Esto se puede hacer mediante scripts SQL para crear nuevas tablas y copiar la información existente. En la ventana "Explorador de servidores", expanda "DefaultConnection" para mostrar las tablas. Haga clic con el botón derecho en Tablas y seleccione "Nueva consulta".

Screenshot of updating the schema in the Identity database by selecting New Query.

Pegue el script SQL de https://raw.github.com/suhasj/UniversalProviders-Identity-Migrations/master/Migration.txt y ejecútelo. Si se actualiza "DefaultConnection", podemos ver que se agregan las nuevas tablas. Puede comprobar los datos dentro de las tablas para ver que se ha migrado la información.

Screenshot of the Default Connection refreshed and new tables added.

Migración de la aplicación para usar ASP.NET Identity

  1. Instale los paquetes Nuget necesarios para 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

    Puede encontrar más información sobre cómo administrar paquetes Nuget aquí

  2. Para trabajar con datos existentes en la tabla, es necesario crear clases de modelo que se asignen de nuevo a las tablas y enlazarlos en el sistema de identidades. Como parte del contrato identity, las clases de modelo deben implementar las interfaces definidas en el archivo dll Identity.Core o pueden ampliar la implementación existente de estas interfaces disponibles en Microsoft.AspNet.Identity.EntityFramework. Usaremos las clases existentes para roles, inicios de sesión de usuario y notificaciones de usuario. Necesitamos usar un usuario personalizado para nuestro ejemplo. Haga clic con el botón derecho en el proyecto y cree una nueva carpeta "IdentityModels". Agregue una nueva clase de "Usuario", como se muestra a continuación:

    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; }
        }
    }
    

    Observe que 'ProfileInfo' es ahora una propiedad en la clase de usuario. Por lo tanto, podemos usar la clase de usuario para trabajar directamente con datos de perfil.

Copie los archivos de las carpetas IdentityModels e IdentityAccount desde el origen de descarga ( https://github.com/suhasj/UniversalProviders-Identity-Migrations/tree/master/UniversalProviders-Identity-Migrations ). Estas tienen las clases de modelo restantes y las nuevas páginas necesarias para la administración de roles y usuarios mediante las API de identidad de ASP.NET. El enfoque usado es similar a la pertenencia a SQL y la explicación detallada se puede encontrar aquí.

Algunos comandos no se admiten si la aplicación usa SQLite como almacén de datos identity. Debido a las limitaciones del motor de base de datos, Alter los comandos producen la siguiente excepción:

"System.NotSupportedException: SQLite no admite esta operación de migración".

Como solución alternativa, ejecute migraciones de Code First en la base de datos para cambiar las tablas.

Copiar datos de perfil en las nuevas tablas

Como se mencionó anteriormente, es necesario deserializar los datos xml en las tablas Perfiles y almacenarlos en las columnas de la tabla AspNetUsers. Las nuevas columnas se crearon en la tabla Usuarios del paso anterior, por lo que todo lo que queda es rellenar esas columnas con los datos necesarios. Para ello, usaremos una aplicación de consola que se ejecuta una vez para rellenar las columnas recién creadas en la tabla Usuarios.

  1. Cree una nueva aplicación de consola en la solución de salida.

    Screenshot of creating a new console application in the exiting solution.

  2. Instale la versión más reciente del paquete de Entity Framework.

  3. Agregue la aplicación web creada anteriormente como referencia a la aplicación de consola. Para ello, haga clic con el botón derecho del ratón en Proyecto, luego en "Añadir referencias", a continuación en Solución, después en el proyecto y haga clic en Aceptar.

  4. Copie el código siguiente en la clase Program.cs. Esta lógica lee los datos de perfil de cada usuario, lo serializa como objeto "ProfileInfo" y lo almacena de nuevo en la base de datos.

    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();
    }
    

    Algunos de los modelos usados se definen en la carpeta "IdentityModels" del proyecto de aplicación web, por lo que debe incluir los espacios de nombres correspondientes.

  5. El código anterior funciona en el archivo de base de datos de la carpeta App_Data del proyecto de aplicación web creado en los pasos anteriores. Para hacer referencia a esto, actualice la cadena de conexión en el archivo app.config de la aplicación de consola con la cadena de conexión en web.config de la aplicación web. Proporcione también la ruta de acceso física completa en la propiedad "AttachDbFilename".

  6. Abra un símbolo del sistema y vaya a la carpeta bin de la aplicación de consola anterior. Ejecute el archivo ejecutable y revise la salida del registro como se muestra en la imagen siguiente.

    Screenshot of the executable in the command prompt to run and review the log output.

  7. Abra la tabla "AspNetUsers" en el Explorador de servidores y compruebe los datos de las nuevas columnas que contienen las propiedades. Deben actualizarse con los valores de propiedad correspondientes.

Comprobación de la funcionalidad

Use las páginas de pertenencia recién agregadas que se implementan mediante ASP.NET Identity para iniciar sesión un usuario de la base de datos anterior. El usuario debe poder iniciar sesión con las mismas credenciales. Pruebe las otras funcionalidades, como agregar OAuth, crear un nuevo usuario, cambiar una contraseña, agregar roles, agregar usuarios a roles, etc.

Los datos de perfil del usuario antiguo y los nuevos usuarios deben recuperarse y almacenarse en la tabla Usuarios. Ya no se debe hacer referencia a la tabla anterior.

Conclusión

En el artículo se describe el proceso de migración de aplicaciones web que usan el modelo de proveedor para la pertenencia a ASP.NET Identity. En el artículo se describe además la migración de datos de perfil para que los usuarios se enlacen al sistema de identidades. Deje los comentarios a continuación para ver las preguntas y los problemas detectados al migrar la aplicación.

Gracias a Rick Anderson y Robert McMurray por revisar el artículo.