2014-07-18 25 views
10

Possiedo un progetto ASP.NET MVC 5 (motore del rasoio) con Identity 2.0 con account utente individuali. Sto usando Visual Studio Professional 2013Database Seeding Identity 2.0

Non ho trovato alcun esempio chiaro (perché non viene fuori dalla scatola?) Di HOW posso seminare il database di Identity 2.0 e tutti gli esempi che vedo sono supportati a metà perché non dire DOVE esattamente devi implementarlo.

Ho utilizzato enable-migrations che ha creato una cartella Migrations con un file Configuration.cs. Ha un override del metodo Seed ma quando inserisco un breakpoint si accorge che non viene mai eseguito, infatti il ​​database Identity non è nemmeno popolato con lo schema.

Quindi, dove e cosa devo fare in modo che lo schema di Identity 2.0 venga creato sul database la prima volta (la stringa di connessione è corretta e il database vuoto esiste). E come monto la Semina?

Su IdentityModels.cs ho questo: public class ApplicationDbContext: IdentityDbContext { PUBBLICA ApplicationDbContext(): base ("DefaultConnection", throwIfV1Schema: false) {}

public static ApplicationDbContext Create() { 
     return new ApplicationDbContext(); 
    } 

    protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder) { 
      base.OnModelCreating(modelBuilder); 
      // to avoid the "has no keys" errors when running Update-Database on PM 
      modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id).ToTable("AspNetRoles"); 
      modelBuilder.Entity<IdentityUser>().ToTable("AspNetUsers"); 
      modelBuilder.Entity<IdentityUserLogin>().HasKey(l => new { l.UserId, l.LoginProvider, l.ProviderKey }).ToTable("AspNetUserLogins"); 
      modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId }).ToTable("AspNetUserRoles"); 
      modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims"); 
    } 
}  

In Migrazioni/configurazione. cs (aggiunto da PM> Enable-Migrazioni) ho questo:

internal sealed class Configuration : DbMigrationsConfiguration<Models.ApplicationDbContext> { 
    public Configuration() { 
     AutomaticMigrationsEnabled = false; 
    } 

    protected override void Seed(Models.ApplicationDbContext context) { 
     WriteReferenceData(); 
    } 
} 

Nei miei Global.asax.cs file sul metodo Application() ho aggiunto questo:

System.Data.Entity.Database.SetInitializer<Models.ApplicationDbContext>(new System.Data.Entity.MigrateDatabaseToLatestVersion<Models.ApplicationDbContext, Migrations.Configuration>()); 

E in IdentityConfig.cs ho questo DB Initializer così anche se sembra essere orfana perché io non so dove per collegare questo:

public class ApplicationDbInitializer : System.Data.Entity.DropCreateDatabaseIfModelChanges<Models.ApplicationDbContext> { 
    protected override void Seed(ApplicationDbContext context) { 
     WriteReferenceData(); 
     base.Seed(context); 
    } 
} 

E infine il metodo WriteReferenceData è in qualche altra classe che lo fa più o meno:

System.Data.Entity.DbContextTransaction transaction = null; 
try { 
    System.Data.Entity.DbContext ctx = Models.ApplicationDbContext.Create(); 
    transaction = ctx.Database.BeginTransaction(); 
    CreateRoles(ctx); 
    CreateUsers(ctx); 
    CreateRoleAssociations(ctx); 
    ctx.SaveChanges(); 
    transaction.Commit(); 
    succeeded = true; 
} 
catch (Exception ex) { 
    if (transaction != null { transaction.Rollback(); transaction.Dispose(); } 
    succeeed = false; 
} 
return succeeded; 

risposta

14

EF ha two different Seed methods. Uno che viene utilizzato con gli inizializzatori del database e un altro che viene utilizzato con le migrazioni. Dato che hai abilitato le Migrazioni, descriverò come farlo con il metodo Migrations Seed qui ...

Prima di tutto, anche se hai abilitato Migrazioni, per impostazione predefinita EF utilizza ancora l'inizializzatore del database CreateDatabaseIfNotExists. Il che significa che quando si esegue l'app, la prima volta che si accede a ApplicationDbContext, viene chiamato l'inizializzatore e verranno create le tabelle del database dai mapping Code First se le tabelle non esistono già. Non stai vedendo lo schema creato perché probabilmente non hai avuto accesso al contesto db. In una nuova app Web, questo viene in genere attivato la prima volta che si registra un utente o si tenta di accedere.

Per eseguire il seeding delle tabelle Identity ASP.NET, ci sono due cose da fare. Il primo è aggiungere la logica di seed al metodo Seed in Configuration.cs. Il secondo è quello di attivare update-database ... eseguendolo nella Console Gestione pacchetti o utilizzando l'inizializzatore del database MigrateDatabaseToLatestVersion.

Ecco un esempio di ciò che è possibile inserire nel metodo Seed per creare un ruolo e un utente ...

public Configuration() 
{ 
    AutomaticMigrationsEnabled = true; 

    // ... 
} 

protected override void Seed(MyProject.Web.Models.ApplicationDbContext context) 
{ 
    if (!context.Roles.Any()) 
    { 
     var roleStore = new RoleStore<IdentityRole>(context); 
     var roleManager = new RoleManager<IdentityRole>(roleStore); 
     var role = new IdentityRole{ 
      Name = "Administrator" 
     }; 
     roleManager.Create(role); 
    } 

    if (!context.Users.Any()) 
    { 
     var userStore = new UserStore<ApplicationUser>(context); 
     var userManager = new ApplicationUserManager(userStore); 

     var user = new ApplicationUser { 
      Email = "[email protected]", 
      UserName = "SuperUser" 
     }; 
     userManager.Create(user, "MySecretPassword1234"); 
     userManager.AddToRole(user.Id, "Administrator"); 
    } 
} 

Dopo aver fatto questo, è possibile eseguire Update-Database dalla Console di Package Manager di migrare il database per l'ultimo schema e eseguire il metodo Seed.

Oppure si può cambiare EF di utilizzare la MigrateDatabaseToLatestVersion inizializzatore modificando il contesto in IdentityModels.cs ...

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
{ 
    public ApplicationDbContext() 
     : base("DefaultConnection", throwIfV1Schema: false) 
    { 
     Database.SetInitializer<ApplicationDbContext>(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>()); 
    } 
} 

Ora, quando si esegue l'applicazione, la prima volta che il contesto di database viene utilizzato sarà eseguito update-database e semina.

+0

In qualche modo nessuna delle due inizializzatori DB sta ottenendo chiamato. Anche lo schema di base del database Identity 2.0 non viene creato quando viene eseguita l'applicazione. –

+0

Cosa succede quando provi a registrare un account? –

+0

Beh, quello che volevo veramente era il seme "originale" senza migrazione ma dal momento che ho delle migrazioni che lo fanno in questo modo. Ho fatto l'Add-Migration Initial ma quando l'ho seguito da Update-Database ho ricevuto diversi errori come "EntityType 'IdentityUserRole' non ha alcuna chiave definita". –

3

Penso di poter risolvere l'indovinello su PERCHÉ il seme non viene mai attivato: poiché il seme viene chiamato solo quando l'applicazione tenta di connettersi al database e NON quando l'applicazione viene avviata.

Ho creato degli esempi e ho utilizzato il codice correttamente con e senza migrazioni. Ma dal momento che si desidera utilizzarlo con le migrazioni abilitate, di seguito è riportato il codice di esempio.

MOLTO IMPORTANTE: Per visualizzare effettivamente il punto di interruzione all'interno di Seed, eseguire l'applicazione, premere Login e utilizzare le credenziali dalla funzione seed per ottenere l'accesso all'applicazione. Nel caso in cui si ottiene "nome utente o password non validi", attenzione per la proprietà manager.PasswordValidator in IdentityConfig.cs :: Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context).

  1. Creare un nuovo progetto ASP.NET MVC 5 in VS2013

  2. aggiornare tutti i pacchetti usando il comando Update-Package .

  3. Enable-Migrazioni

  4. Nei Configuration.cs creato da migrazioni aggiungere il seguente codice:

    internal sealed class Configuration : DbMigrationsConfiguration 
        { 
         public Configuration() 
         { 
          AutomaticMigrationsEnabled = false; 
         }

    protected override void Seed(ApplicationDbContext context) { bool itWorks = WriteReferenceData(context); base.Seed(context); } private bool WriteReferenceData(ApplicationDbContext ctx) { DbContextTransaction transaction = null; bool succeeded = false; try { transaction = ctx.Database.BeginTransaction(); CreateRoles(ctx); CreateUsers(ctx); ctx.SaveChanges(); transaction.Commit(); succeeded = true; } catch (Exception ex) { if (transaction != null) { transaction.Rollback(); transaction.Dispose(); } succeeded = false; } return succeeded; } private void CreateRoles(ApplicationDbContext ctx) { // Out of the box // ctx.Roles.AddOrUpdate( // new IdentityRole { Name = "Administrator" }, // new IdentityRole { Name = "Guest" } // ); // Another approach var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(ctx)); var roleName = "Administrator"; //Create role if it does not exist if (!RoleManager.RoleExists(roleName)) { var roleresult = RoleManager.Create(new IdentityRole(roleName)); } } private void CreateUsers(ApplicationDbContext ctx) { // Out of the box approach // ctx.Users.AddOrUpdate( // new ApplicationUser { Email = "[email protected]", UserName = "[email protected]" } // ); // Another approach var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(ctx)); var user = new ApplicationUser() { UserName = "[email protected]", Email="[email protected]"}; var password = "[email protected]"; var adminresult = UserManager.Create(user, password); //Add User Admin to Role Administrator if (adminresult.Succeeded) { var result = UserManager.AddToRole(user.Id, "Administrator"); } } }
  5. In Global.asax.cs :: Application_Start(), aggiungere il seguente linea:

    Database.SetInitializer<ApplicationDbContext>(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());

  6. Run!

Se è necessario anche un campione di codice con le migrazioni disabilitate, fatemi sapere.

+0

Ho deciso di eliminare Migrations perché non è adatto per il web hosting condiviso in cui l'app non ha autorizzazioni per creare/eliminare il database. Quindi, sto provando a fare il seed con il metodo non-migrations. –

+0

Ok! Hai ancora bisogno di un campione funzionante di codice per questo? –

+0

Ciao Corneliu, Se non ti dispiace vorrei vedere un esempio di fare questo con le migrazioni disabilitate; o) –

2

Così facciamo qualcosa di simile nel nostro pacchetto campioni nel modo seguente (per seminare il db con il nostro utente admin)

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
{ 
    public ApplicationDbContext() 
     : base("DefaultConnection", throwIfV1Schema: false) 
    { 
    } 

    static ApplicationDbContext() 
    { 
     // Set the database intializer which is run once during application start 
     // This seeds the database with admin user credentials and admin role 
     Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer()); 
    } 

    public static ApplicationDbContext Create() 
    { 
     return new ApplicationDbContext(); 
    } 
} 

// This is useful if you do not want to tear down the database each time you run the application. 
// public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext> 
// This example shows you how to create a new database if the Model changes 
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext> 
{ 
    protected override void Seed(ApplicationDbContext context) { 
     DoYourSeedingHere(context); 
     base.Seed(context); 
    } 

} 
1

Per completare questa domanda .. Come Corneliu Serediuc detto tutto quello che dovete fare è semplicemente tenta di connettersi al database durante l'avvio dell'applicazione, per esempio, come questo (IdentityModel.cs):

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
{ 
    public ApplicationDbContext() 
     : base("DefaultConnection", throwIfV1Schema: false) 
    { 
     Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer()); 
    } 


    public static ApplicationDbContext Create() 
    { 
     var ctx = new ApplicationDbContext(); 
     var runSeed = ctx.Roles.AnyAsync(); 

     return ctx; 
    } 

} a proposito, Corneliu grazie per i vostri esempi di codice, mi aiutano molto.

Problemi correlati