Entity Framework Code-pierwsze problemy (SimpleMembership USERPROFILE table)

Jeśli użyłeś ASP.NET MVC 4 zauważysz, że domyślnym dla aplikacji internetowej jest korzystanie z dostawcy SimpleMembership, to wszystko jest dobrze i dobrze i działa dobrze.

Problem pochodzi z domyślnym generowaniem bazy danych, mają POCO dla UserProfile zdefiniowane w następujący sposób:

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
}

.. który następnie jest generowany w następujący sposób:

using (var context = new UsersContext())
{
    if (!context.Database.Exists())
    {
         // Create the SimpleMembership database without Entity Framework migration schema
         ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
    }
}

To działa dobrze, baza danych jest generowana dobrze i działa bez problemu. Jeśli jednak mam tak zmienić POCO i usunąć baza danych:

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string EmailAddress { get; set; }

    public string FirstName { get; set; }
    public string Surname { get; set; }

    public string Country { get; set; }

    public string CompanyName { get; set; }
}

Generowane są tylko pierwsze 2 kolumny, UserId i EmailAddress. Działa dobrze pod względem kodu (gadające Logowanie/Rejestracja), ale oczywiście żadne z moich innych danych użytkownika nie jest przechowywane.

Czy coś mi umknęło? Z pewnością powinna wygenerować bazę danych na podstawie całego obiektu UserProfile.
Author: Rudi Visser, 2012-09-20

4 answers

1 - Musisz włączyć migracje, najlepiej z EntityFramework 5. Użyj Enable-Migrations w menedżerze pakietów NuGet.

2-Przenieś swoje

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true); 

Do metody Seed w YourMvcApp / Migrations / Configuration.Klasa cs

    protected override void Seed(UsersContext context)
    {
        WebSecurity.InitializeDatabaseConnection(
            "DefaultConnection",
            "UserProfile",
            "UserId",
            "UserName", autoCreateTables: true);

        if (!Roles.RoleExists("Administrator"))
            Roles.CreateRole("Administrator");

        if (!WebSecurity.UserExists("lelong37"))
            WebSecurity.CreateUserAndAccount(
                "lelong37",
                "password",
                new {Mobile = "+19725000000", IsSmsVerified = false});

        if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
            Roles.AddUsersToRoles(new[] {"lelong37"}, new[] {"Administrator"});
    }

Teraz EF5 będzie odpowiedzialny za tworzenie tabeli UserProfile, po zrobieniu tego wywołasz WebSecurity.InitializeDatabaseConnection rejestruje tylko SimpleMembershipProvider z już utworzoną tabelą UserProfile, a także informuje SimpleMembershipProvider, która kolumna jest identyfikatorem użytkownika i nazwą użytkownika. Pokazuję również przykład, w jaki sposób można dodać użytkowników, role i powiązać te dwa w swojej metodzie zalążkowej z niestandardowymi właściwościami/polami UserProfile, np.

3-Teraz, gdy uruchomisz update-database z konsoli Menedżera pakietów, EF5 udostępni Twoją tabelę ze wszystkimi własnymi właściwościami

Aby uzyskać dodatkowe odniesienia, zapoznaj się z tym artykułem z kodem źródłowym: http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/

 47
Author: LeLong37,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-23 20:51:56

Wygląda na to, że w końcu to zrozumiałem i to mogło być tylko jedno wielkie nieporozumienie.

Jak się okazało, spodziewałem się ((IObjectContextAdapter)context).ObjectContext.CreateDatabase(); zrobić to, czego po prostu nie ma, czyli stworzyć wszystkie tabele w bazie danych, które nie istnieją, lub po prostu zaktualizować je, jeśli tak się stanie i są inne.

To, co się naprawdę dzieje, to to, że dosłownie uruchamia CREATE DATABASE oświadczenie, które dla mnie jest najbardziej bezużyteczną rzeczą w historii. Jeśli nie pracujesz w naprawdę dziwnym środowisku, zawsze będziesz mieć bazę danych na wyłączonym i tak będzie zawsze istnieć (a następnie tworzenie tabeli nigdy nie nastąpi!), Wolałbym i tak nie dawać realnym użytkownikom dostępu do tworzenia bazy danych.

W każdym razie, rozwiązałem mój konkretny problem, chcąc UserProfile (i powiązanych tabel) do tworzenia bazy danych za pomocą inicjalizatora DropCreateDatabaseIfModelChanges i wymuszając inicjalizację jak poniżej:

public SimpleMembershipInitializer()
{
#if DEBUG
    Database.SetInitializer<DataContext>(new DropCreateDatabaseIfModelChanges<DataContext>());
#else
    Database.SetInitializer<DataContext>(null);
#endif

    try
    {
        using (var context = new DataContext())
        {
            if (!context.Database.Exists())
            {
                ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
            }
            context.Database.Initialize(true);
        }

        WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true);
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
    }
}

.. to działa i jest idealny do rozwoju, ale jest dość bezużyteczny w praktyce, ponieważ będzie dosłownie upuść bazę danych i odtworz ją od zera, jeśli model ulegnie zmianie. Dla mnie to sprawia, że cały kod-pierwsza praktyka prawie bezużyteczna w jego domyślnej formie i prawdopodobnie skończę z powrotem do generacji z-DB edmx.

"tajemnicą" za UserProfile wciąż tworzoną tabelą jest to, że WebSecurity.InitializeDatabaseConnection zainicjalizuje tabelę, jeśli nie istnieje w oparciu o pola, które do niej przekazujesz, dlatego EmailAddress została utworzona zamiast UserName, ponieważ zmieniłem ją w tym.

 5
Author: Rudi Visser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-23 22:47:51

Miałem ten sam problem. Dodałem kod, aby migracje miały miejsce tuż przed "CreateDatabase" w SimpleMembershipInitializer.

To naprawiło problem dla mnie, z tym wyjątkiem, że wierzę, że teraz moje migracje będą stosowane na platformie Azure bez względu na ustawienie w profilu publikowania.

  private class SimpleMembershipInitializer
    {
        public SimpleMembershipInitializer()
        {
            Database.SetInitializer<WomContext>(null);

            // forcing the application of the migrations so the users table is modified before
            // the code below tries to create it. 
            var migrations = new MigrateDatabaseToLatestVersion<WomContext, Wom.Migrations.Configuration>();
            var context = new WomContext(); 
            migrations.InitializeDatabase(context); 

            try
            {....
 2
Author: rufo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-21 23:39:29

Jeśli nie masz planów wprowadzania zmian po uruchomieniu systemu i dzieje się to tylko w fazie rozwoju i nie chcesz włączyć migracji. Spróbuj obciąć tabelę _ _ MigrationHistory.

truncate table __MigrationHistory
 0
Author: Siva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-04 06:49:41