Migrowanie danych uniwersalnego dostawcy dotyczących członkostwa i profilów użytkowników do systemu ASP.NET Identity (C#)

Pranav Rastogi, Rick Anderson, Robert McMurray, Suhas Joshi

W tym samouczku opisano kroki niezbędne do migracji danych użytkownika i roli oraz danych profilu użytkownika utworzonych przy użyciu dostawców uniwersalnych istniejącej aplikacji do modelu tożsamości ASP.NET. Podejście wymienione tutaj do migrowania danych profilu użytkownika może być również używane w aplikacji z członkostwem SQL.

Wraz z wydaniem Visual Studio 2013 zespół ASP.NET wprowadził nowy system ASP.NET Identity i możesz dowiedzieć się więcej o tej wersji tutaj. Poniżej przedstawiono procedurę migracji aplikacji internetowych z programu SQL Membership do nowego systemu tożsamości. W tym artykule przedstawiono kroki migracji istniejących aplikacji, które są zgodne z modelem dostawcy na potrzeby zarządzania użytkownikami i rolami w nowym modelu tożsamości. W tym samouczku skupimy się przede wszystkim na migrowaniu danych profilu użytkownika, aby bezproblemowo podłączyć je do nowego systemu. Migrowanie informacji o użytkowniku i roli jest podobne do członkostwa w programie SQL. Podejście stosowane do migracji danych profilu może być również używane w aplikacji z członkostwem SQL.

Na przykład zaczniemy od aplikacji internetowej utworzonej przy użyciu programu Visual Studio 2012, która korzysta z modelu Dostawcy. Następnie dodamy kod do zarządzania profilami, zarejestrujemy użytkownika, dodamy dane profilu dla użytkowników, zmigrujemy schemat bazy danych, a następnie zmienimy aplikację, aby korzystała z systemu tożsamości na potrzeby zarządzania użytkownikami i rolami. Podczas testowania migracji użytkownicy utworzoni przy użyciu dostawców uniwersalnych powinni mieć możliwość logowania się, a nowi użytkownicy powinni mieć możliwość rejestracji.

Uwaga

Pełny przykład można znaleźć na stronie https://github.com/suhasj/UniversalProviders-Identity-Migrations.

Podsumowanie migracji danych profilu

Przed rozpoczęciem migracji przyjrzyjmy się doświadczeniu przechowywania danych profilu w modelu Dostawcy. Dane profilów dla użytkowników aplikacji mogą być przechowywane na wiele sposobów, najczęściej używane przez dostawców wbudowanych profilów dostarczanych wraz z dostawcami uniwersalnymi. Kroki obejmowałyby następujące czynności:

  1. Dodaj klasę, która ma właściwości używane do przechowywania danych profilu.
  2. Dodaj klasę, która rozszerza metodę "ProfileBase" i implementuje metody pobierania powyższych danych profilu dla użytkownika.
  3. Włącz używanie domyślnych dostawców profilów w pliku web.config i zdefiniuj klasę zadeklarowaną w kroku 2, która ma być używana w uzyskiwaniu dostępu do informacji o profilu.

Informacje o profilu są przechowywane jako serializowane dane xml i binarne w tabeli "Profile" w bazie danych.

Po przeprowadzeniu migracji aplikacji do korzystania z nowego systemu ASP.NET Identity informacje o profilu są deserializowane i przechowywane jako właściwości w klasie użytkownika. Każda właściwość może być następnie mapowana na kolumny w tabeli użytkownika. Zaletą jest to, że właściwości można pracować bezpośrednio przy użyciu klasy użytkownika, oprócz braku konieczności serializacji/deserializacji informacji o danych za każdym razem, gdy uzyskujesz do niej dostęp.

Getting Started

  1. Utwórz nową aplikację ASP.NET 4.5 Web Forms w programie Visual Studio 2012. W bieżącym przykładzie użyto szablonu Web Forms, ale można również użyć aplikacji MVC.

    Zrzut ekranu przedstawiający nowo utworzoną aplikację Web Forms w programie Visual Studio 2012 przy użyciu szablonu Web Forms.

  2. Tworzenie nowego folderu "Models" w celu przechowywania informacji o profilu

    Zrzut ekranu przedstawiający nowy folder o nazwie Modele utworzone do przechowywania informacji o profilu.

  3. Na przykład zapiszmy datę urodzenia, miasto, wysokość i wagę użytkownika w profilu. Wysokość i waga są przechowywane jako klasa niestandardowa o nazwie "PersonalStats". Aby przechowywać i pobierać profil, potrzebujemy klasy, która rozszerza klasę "ProfileBase". Utwórzmy nową klasę "AppProfile", aby pobrać i zapisać informacje o profilu.

    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. Włącz profil w pliku web.config . Wprowadź nazwę klasy, która ma być używana do przechowywania/pobierania informacji o użytkowniku utworzonych w kroku 3.

    <profile defaultProvider="DefaultProfileProvider" enabled="true"
        inherits="UniversalProviders_ProfileMigrations.Models.AppProfile">
      <providers>
        .....
      </providers>
    </profile>
    
  5. Dodaj stronę formularzy internetowych w folderze "Konto", aby pobrać dane profilu od użytkownika i zapisać je. Kliknij prawym przyciskiem myszy projekt i wybierz pozycję "Dodaj nowy element". Dodaj nową stronę formularzy internetowych ze stroną wzorcową "AddProfileData.aspx". Skopiuj następujące elementy w sekcji "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>
    

    Dodaj następujący kod w kodzie za:

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

    Dodaj przestrzeń nazw, w której zdefiniowano klasę AppProfile, aby usunąć błędy kompilacji.

  6. Uruchom aplikację i utwórz nowego użytkownika z nazwą użytkownika "olduser". Przejdź do strony "AddProfileData" i dodaj informacje o profilu użytkownika.

    Zrzut ekranu przedstawiający stronę Dodawanie danych profilu w celu dodania informacji o profilu użytkownika.

Możesz sprawdzić, czy dane są przechowywane jako serializowane xml w tabeli "Profile" przy użyciu okna Eksplorator serwera. W programie Visual Studio z menu "Widok" wybierz pozycję "Eksplorator serwera". W pliku web.config powinna istnieć połączenie danych dla bazy danych. Kliknięcie połączenia danych spowoduje wyświetlenie różnych podkategorii. Rozwiń węzeł "Tabele", aby wyświetlić różne tabele w bazie danych, a następnie kliknij prawym przyciskiem myszy pozycję "Profile" i wybierz pozycję "Pokaż dane tabeli", aby wyświetlić dane profilu przechowywane w tabeli Profile.

Zrzut ekranu przedstawiający okno Eksplorator serwera zawierające dane przechowywane w tabeli

Zrzut ekranu przedstawiający tabelę danych Profile.

Migrowanie schematu bazy danych

Aby istniejąca baza danych działała z systemem Identity, musimy zaktualizować schemat w bazie danych Identity, aby obsługiwać pola dodane do oryginalnej bazy danych. Można to zrobić przy użyciu skryptów SQL w celu utworzenia nowych tabel i skopiowania istniejących informacji. W oknie "Eksplorator serwera" rozwiń węzeł "DefaultConnection", aby wyświetlić tabele. Kliknij prawym przyciskiem myszy pozycję Tabele i wybierz pozycję "Nowe zapytanie"

Zrzut ekranu przedstawiający aktualizowanie schematu w bazie danych tożsamości, wybierając pozycję Nowe zapytanie.

Wklej skrypt SQL z https://raw.github.com/suhasj/UniversalProviders-Identity-Migrations/master/Migration.txt i uruchom go. Jeśli odświeżono element "DefaultConnection", zobaczymy, że dodano nowe tabele. Możesz sprawdzić dane w tabelach, aby zobaczyć, że informacje zostały zmigrowane.

Zrzut ekranu przedstawiający odświeżone domyślne połączenie i dodane nowe tabele.

Migrowanie aplikacji do korzystania z usługi ASP.NET Identity

  1. Zainstaluj pakiety NuGet wymagane do 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

    Więcej informacji na temat zarządzania pakietami Nuget można znaleźć tutaj

  2. Aby pracować z istniejącymi danymi w tabeli, musimy utworzyć klasy modelu, które są mapowania z powrotem do tabel i podłączać je do systemu Tożsamości. W ramach kontraktu tożsamości klasy modelu powinny implementować interfejsy zdefiniowane w bibliotece DLL Identity.Core lub rozszerzyć istniejącą implementację tych interfejsów dostępnych w elemecie Microsoft.AspNet.Identity.EntityFramework. Będziemy używać istniejących klas dla ról, identyfikatorów logowania użytkowników i oświadczeń użytkowników. Musimy użyć niestandardowego użytkownika dla naszego przykładu. Kliknij projekt prawym przyciskiem myszy i utwórz nowy folder "IdentityModels". Dodaj nową klasę "User", jak pokazano poniżej:

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

    Zwróć uwagę, że właściwość "ProfileInfo" jest teraz właściwością klasy użytkownika. W związku z tym możemy użyć klasy użytkownika do bezpośredniej pracy z danymi profilu.

Skopiuj pliki w folderach IdentityModels i IdentityAccount ze źródła pobierania ( https://github.com/suhasj/UniversalProviders-Identity-Migrations/tree/master/UniversalProviders-Identity-Migrations ). Mają one pozostałe klasy modeli i nowe strony potrzebne do zarządzania użytkownikami i rolami przy użyciu interfejsów API ASP.NET Identity. Używane podejście jest podobne do członkostwa SQL i szczegółowe wyjaśnienie można znaleźć tutaj.

Niektóre polecenia nie są obsługiwane, jeśli aplikacja używa biblioteki SQLite jako magazynu danych identity. Ze względu na ograniczenia aparatu bazy danych Alter polecenia zgłaszają następujący wyjątek:

"System.NotSupportedException: SQLite nie obsługuje tej operacji migracji".

W miarę pracy uruchom migracje Code First w bazie danych, aby zmienić tabele.

Kopiowanie danych profilu do nowych tabel

Jak wspomniano wcześniej, musimy deserializować dane xml w tabelach Profiles i przechowywać je w kolumnach tabeli AspNetUsers. Nowe kolumny zostały utworzone w tabeli users w poprzednim kroku, aby wszystkie pozostałe kolumny były wypełniane danymi niezbędnymi. W tym celu użyjemy aplikacji konsolowej, która zostanie uruchomiona raz, aby wypełnić nowo utworzone kolumny w tabeli użytkowników.

  1. Utwórz nową aplikację konsolową w rozwiązaniu zakończenia.

    Zrzut ekranu przedstawiający tworzenie nowej aplikacji konsolowej w rozwiązaniu zakończenia.

  2. Zainstaluj najnowszą wersję pakietu Entity Framework.

  3. Dodaj aplikację internetową utworzoną powyżej jako odwołanie do aplikacji konsolowej. Aby to zrobić, kliknij prawym przyciskiem myszy pozycję Projekt, a następnie pozycję "Dodaj odwołania", a następnie kliknij projekt, a następnie kliknij przycisk OK.

  4. Skopiuj poniższy kod w klasie Program.cs. Ta logika odczytuje dane profilu dla każdego użytkownika, serializuje je jako obiekt "ProfileInfo" i przechowuje je z powrotem do bazy danych.

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

    Niektóre używane modele są definiowane w folderze "IdentityModels" projektu aplikacji internetowej, dlatego należy uwzględnić odpowiednie przestrzenie nazw.

  5. Powyższy kod działa na pliku bazy danych w folderze App_Data projektu aplikacji internetowej utworzonego w poprzednich krokach. Aby to odwołać, zaktualizuj parametry połączenia w pliku app.config aplikacji konsolowej za pomocą parametrów połączenia w web.config aplikacji internetowej. Podaj również pełną ścieżkę fizyczną we właściwości "AttachDbFilename".

  6. Otwórz wiersz polecenia i przejdź do folderu bin powyższej aplikacji konsolowej. Uruchom plik wykonywalny i przejrzyj dane wyjściowe dziennika, jak pokazano na poniższej ilustracji.

    Zrzut ekranu przedstawiający plik wykonywalny w wierszu polecenia, aby uruchomić i przejrzeć dane wyjściowe dziennika.

  7. Otwórz tabelę "AspNetUsers" w Eksploratorze serwera i sprawdź dane w nowych kolumnach, które przechowują właściwości. Należy je zaktualizować przy użyciu odpowiednich wartości właściwości.

Weryfikowanie funkcjonalności

Użyj nowo dodanych stron członkostwa, które są implementowane przy użyciu ASP.NET Identity, aby zalogować użytkownika ze starej bazy danych. Użytkownik powinien mieć możliwość logowania się przy użyciu tych samych poświadczeń. Wypróbuj inne funkcje, takie jak dodawanie protokołu OAuth, tworzenie nowego użytkownika, zmienianie hasła, dodawanie ról, dodawanie użytkowników do ról itp.

Dane profilu dla starego użytkownika i nowych użytkowników powinny być pobierane i przechowywane w tabeli użytkowników. Stara tabela nie powinna już być przywołyna.

Podsumowanie

W artykule opisano proces migrowania aplikacji internetowych, które używały modelu dostawcy do członkostwa w usłudze ASP.NET Identity. Artykuł zawiera również opis migrowania danych profilu dla użytkowników, które mają być przyłączone do systemu tożsamości. W przypadku pytań i problemów napotkanych podczas migracji aplikacji pozostaw poniższe komentarze.

Dzięki Rick Anderson i Robert McMurray do przejrzenia artykułu.