Migrazione dei dati dei provider universali per l'appartenenza e i profili utente ad ASP.NET Identity (C#)
di Pranav Rastogi, Rick Anderson, Robert McMurray, Suhas Joshi
Questa esercitazione descrive i passaggi necessari per eseguire la migrazione dei dati utente e del ruolo e dei dati del profilo utente creati usando provider universali di un'applicazione esistente al modello di identità ASP.NET. L'approccio indicato qui per eseguire la migrazione dei dati del profilo utente può essere usato anche in un'applicazione con appartenenza a SQL.
Con la versione di Visual Studio 2013, il team di ASP.NET ha introdotto un nuovo sistema di identità ASP.NET e è possibile leggere altre informazioni sulla versione qui. In seguito all'articolo per eseguire la migrazione di applicazioni Web dall'appartenenza SQL al nuovo sistema di identità, questo articolo illustra i passaggi per eseguire la migrazione di applicazioni esistenti che seguono il modello Provider per la gestione degli utenti e dei ruoli al nuovo modello di identità. L'obiettivo di questa esercitazione sarà principalmente la migrazione dei dati del profilo utente per collegarlo senza problemi al nuovo sistema. La migrazione di informazioni sull'utente e sul ruolo è simile per l'appartenenza a SQL. L'approccio seguito per eseguire la migrazione dei dati del profilo può essere usato anche in un'applicazione con appartenenza SQL.
Ad esempio, si inizierà con un'app Web creata usando Visual Studio 2012 che usa il modello Provider. Verrà quindi aggiunto codice per la gestione dei profili, registrare un utente, aggiungere dati del profilo per gli utenti, eseguire la migrazione dello schema del database e quindi modificare l'applicazione per usare il sistema Identity per la gestione degli utenti e dei ruoli. Come test della migrazione, gli utenti creati usando provider universali devono essere in grado di accedere e i nuovi utenti devono essere in grado di registrare.
Nota
È possibile trovare l'esempio completo in https://github.com/suhasj/UniversalProviders-Identity-Migrations.
Riepilogo della migrazione dei dati del profilo
Prima di iniziare con le migrazioni, esaminare l'esperienza di archiviazione dei dati del profilo nel modello Provider. I dati del profilo per gli utenti dell'applicazione possono essere archiviati in più modi, il più comune tra loro è l'uso dei provider di profili predefiniti forniti insieme alla provider universali. I passaggi includono
- Aggiungere una classe con proprietà usate per archiviare i dati del profilo.
- Aggiungere una classe che estende 'ProfileBase' e implementa i metodi per ottenere i dati del profilo precedenti per l'utente.
- Abilitare l'uso di provider di profili predefiniti nel file web.config e definire la classe dichiarata nel passaggio 2 da usare per accedere alle informazioni sul profilo.
Le informazioni sul profilo vengono archiviate come dati xml serializzati e binari nella tabella "Profili" nel database.
Dopo aver eseguito la migrazione dell'applicazione per usare il nuovo sistema di ASP.NET Identity, le informazioni sul profilo vengono deserializzate e archiviate come proprietà nella classe utente. Ogni proprietà può quindi essere mappata alle colonne nella tabella utente. Il vantaggio è che le proprietà possono essere eseguite direttamente usando la classe utente oltre a non dover serializzare/deserializzare le informazioni sui dati ogni volta che lo si accede.
Introduzione
Creare una nuova applicazione ASP.NET 4.5 Web Forms in Visual Studio 2012. L'esempio corrente usa anche il modello di Web Forms, ma è possibile usare anche l'applicazione MVC.
Creare una nuova cartella 'Models' per archiviare le informazioni sul profilo
Ad esempio, è possibile archiviare la data di nascita, la città, l'altezza e il peso dell'utente nel profilo. L'altezza e il peso vengono archiviati come classe personalizzata denominata "PersonalStats". Per archiviare e recuperare il profilo, è necessaria una classe che estende "ProfileBase". Verrà creata una nuova classe 'AppProfile' per ottenere e archiviare le informazioni sul profilo.
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); } }
Abilitare il profilo nel file diweb.config . Immettere il nome della classe da usare per archiviare/recuperare le informazioni utente create nel passaggio 3.
<profile defaultProvider="DefaultProfileProvider" enabled="true" inherits="UniversalProviders_ProfileMigrations.Models.AppProfile"> <providers> ..... </providers> </profile>
Aggiungere una pagina web form nella cartella "Account" per ottenere i dati del profilo dall'utente e archiviarlo. Fare clic con il pulsante destro del mouse sul progetto e selezionare "Aggiungi nuovo elemento". Aggiungere una nuova pagina webforms con la pagina master 'AddProfileData.aspx'. Copiare quanto segue nella sezione '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>
Aggiungere il codice seguente nel 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(); }
Aggiungere lo spazio dei nomi in cui è definita la classe AppProfile per rimuovere gli errori di compilazione.
Eseguire l'app e creare un nuovo utente con nome utente 'olduser'. Passare alla pagina 'AddProfileData' e aggiungere informazioni sul profilo per l'utente.
È possibile verificare che i dati vengano archiviati come xml serializzati nella tabella 'Profili' usando la finestra Esplora server. In Visual Studio, dal menu 'Visualizza', scegliere 'Esplora server'. Deve essere presente una connessione dati per il database definito nel file diweb.config . Facendo clic sulla connessione dati vengono visualizzate diverse categorie secondarie. Espandere "Tabelle" per visualizzare le diverse tabelle nel database, quindi fare clic con il pulsante destro del mouse su "Profili" e scegliere "Mostra dati tabella" per visualizzare i dati del profilo archiviati nella tabella Profili.
Migrazione dello schema del database
Per rendere funzionante il database esistente con il sistema Identity, è necessario aggiornare lo schema nel database Identity per supportare i campi aggiunti al database originale. Questa operazione può essere eseguita usando script SQL per creare nuove tabelle e copiare le informazioni esistenti. Nella finestra "Esplora server" espandere "DefaultConnection" per visualizzare le tabelle. Fare clic con il pulsante destro del mouse su Tabelle e scegliere "Nuova query"
Incollare lo script SQL da https://raw.github.com/suhasj/UniversalProviders-Identity-Migrations/master/Migration.txt ed eseguirlo. Se viene aggiornato il valore "DefaultConnection", è possibile notare che vengono aggiunte le nuove tabelle. È possibile controllare i dati all'interno delle tabelle per verificare che le informazioni siano state migrate.
Migrazione dell'applicazione per l'uso di identità ASP.NET
Installare i pacchetti Nuget necessari per 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
Altre informazioni sulla gestione dei pacchetti Nuget sono disponibili qui
Per usare i dati esistenti nella tabella, è necessario creare classi di modello che vengono mappate di nuovo alle tabelle e collegarle nel sistema Identity. Come parte del contratto Identity, le classi di modello devono implementare le interfacce definite nella dll Identity.Core o estendere l'implementazione esistente di queste interfacce disponibili in Microsoft.AspNet.Identity.EntityFramework. Verranno usati le classi esistenti per il ruolo, gli account di accesso utente e le attestazioni utente. È necessario usare un utente personalizzato per l'esempio. Fare clic con il pulsante destro del mouse sul progetto e creare una nuova cartella 'IdentityModels'. Aggiungere una nuova classe 'User' come illustrato di seguito:
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; } } }
Si noti che "ProfileInfo" è ora una proprietà nella classe utente. Di conseguenza, è possibile usare la classe utente per lavorare direttamente con i dati del profilo.
Copiare i file nelle cartelle IdentityModels e IdentityAccount dall'origine di download ( https://github.com/suhasj/UniversalProviders-Identity-Migrations/tree/master/UniversalProviders-Identity-Migrations ). Sono disponibili le classi di modello rimanenti e le nuove pagine necessarie per la gestione degli utenti e dei ruoli usando le API di ASP.NET Identity. L'approccio usato è simile all'appartenenza SQL e la spiegazione dettagliata è disponibile qui.
Alcuni comandi non sono supportati se l'app usa SQLite come archivio dati Identity. A causa delle limitazioni nel motore di database,
Alter
i comandi generano l'eccezione seguente:"System.NotSupportedException: SQLite non supporta questa operazione di migrazione".
In seguito, eseguire code First migrations nel database per modificare le tabelle.
Copia dei dati del profilo nelle nuove tabelle
Come accennato in precedenza, è necessario deserializzare i dati xml nelle tabelle Profili e archiviarlo nelle colonne della tabella AspNetUsers. Le nuove colonne sono state create nella tabella degli utenti nel passaggio precedente, quindi tutto ciò che viene lasciato consiste nel popolare tali colonne con i dati necessari. A tale scopo, verrà usata un'applicazione console eseguita una volta per popolare le colonne appena create nella tabella utenti.
Creare una nuova applicazione console nella soluzione di uscita.
Installare la versione più recente del pacchetto Entity Framework.
Aggiungere l'applicazione Web creata in precedenza come riferimento all'applicazione console. A tale scopo, fare clic con il pulsante destro del mouse su Progetto, quindi su "Aggiungi riferimenti", quindi su Soluzione, quindi fare clic sul progetto e scegliere OK.
Copiare il codice seguente nella classe Program.cs. Questa logica legge i dati del profilo per ogni utente, serializzandolo come oggetto 'ProfileInfo' e lo archivia nuovamente nel database.
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(); }
Alcuni dei modelli usati sono definiti nella cartella 'IdentityModels' del progetto applicazione Web, quindi è necessario includere gli spazi dei nomi corrispondenti.
Il codice precedente funziona nel file di database nella cartella App_Data del progetto applicazione Web creato nei passaggi precedenti. Per fare riferimento a questo, aggiornare la stringa di connessione nel file app.config dell'applicazione console con la stringa di connessione nella web.config dell'applicazione Web. Specificare anche il percorso fisico completo nella proprietà "AttachDbFilename".
Aprire un prompt dei comandi e passare alla cartella bin dell'applicazione console precedente. Eseguire il file eseguibile e esaminare l'output del log, come illustrato nell'immagine seguente.
Aprire la tabella 'AspNetUsers' in Esplora server e verificare i dati nelle nuove colonne che contengono le proprietà. Devono essere aggiornati con i valori delle proprietà corrispondenti.
Verificare la funzionalità
Usare le pagine di appartenenza appena aggiunte implementate usando ASP.NET Identity per accedere a un utente dal database precedente. L'utente deve essere in grado di accedere usando le stesse credenziali. Provare le altre funzionalità come l'aggiunta di OAuth, la creazione di un nuovo utente, la modifica di una password, l'aggiunta di ruoli, l'aggiunta di utenti ai ruoli e così via.
I dati del profilo per l'utente precedente e i nuovi utenti devono essere recuperati e archiviati nella tabella utenti. La tabella precedente non deve più essere a cui fare riferimento.
Conclusione
L'articolo descrive il processo di migrazione di applicazioni Web che hanno usato il modello di provider per l'appartenenza a ASP.NET Identity. L'articolo descrive inoltre la migrazione dei dati del profilo per gli utenti da collegare al sistema Identity. Lasciare i commenti seguenti per domande e problemi riscontrati quando si esegue la migrazione dell'app.
Grazie a Rick Anderson e Robert McMurray per la revisione dell'articolo.