2014-07-18 94 views
10

我有一个ASP.NET MVC 5项目(剃须刀引擎),它具有身份2.0个人用户帐户。我正在使用Visual Studio Professional 2013种子标识2.0数据库

我还没有找到任何明确的示例(为什么它不是开箱即用?)如何为Identity 2.0数据库创建种子,并且我看到的所有示例都有一半支持,因为它们不要说在哪里你必须实现这一点。

我使用了使用Configuration.cs文件创建Migrations文件夹的enable-migrations。它有一个Seed方法覆盖,但是当我放置一个断点时,它会注意到它永远不会被执行,事实上,Identity数据库甚至不会被模式填充。

因此,我必须做什么以便第一次在数据库上创建Identity 2.0模式(连接字符串正确且存在空数据库)。我如何装配播种?

在IdentityModels.cs我有这样的: 公共类ApplicationDbContext:IdentityDbContext { ublic ApplicationDbContext():基地( “DefaultConnection”,throwIfV1Schema:假) {}

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

在迁移/配置。 CS(由PM添加>启用的迁移),我有这样的:在我加入这个的Application_Start()方法

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

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

在我的Global.asax.cs文件:

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

而在IdentityConfig.cs我有这个DB初始化程序以及虽然它似乎是孤儿,因为我不知道在哪里插上这样的:

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

最后的WriteReferenceData方法在一些其他类中,这或多或少地做到这一点:

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; 

回答

14

EF有two different Seed methods。一个用于数据库初始化程序,另一个用于迁移。由于您已启用迁移,因此我将在此处描述如何使用迁移种子方法执行此操作...

首先,即使已启用迁移,默认情况下EF仍使用CreateDatabaseIfNotExists数据库初始值设定项。这意味着当你运行你的应用程序时,第一次访问ApplicationDbContext时,初始化程序会被调用,如果这些表已经不存在,它将从Code First映射中创建数据库表。您没有看到创建的模式,因为您可能没有访问数据库上下文。在新的Web应用程序中,这通常会在您第一次注册用户或尝试登录时触发。

要种子ASP.NET身份表,您需要执行两项操作。首先是在Configuration.cs中将种子逻辑添加到Seed方法。第二种方法是触发update-database ...通过在程序包管理器控制台中运行它或使用数据库初始化程序MigrateDatabaseToLatestVersion

下面是可以放入Seed方法以创建角色和用户的示例...

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

完成上述操作后,就可以从包管理器控制台运行Update-Database将数据库迁移到最新的架构和运行种子法。

或者你也可以改变EF通过修改IdentityModels.cs上下文中使用MigrateDatabaseToLatestVersion初始化...

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

现在,当你运行应用程序,第一时间数据库上下文中使用它会运行update-database并种下它。

+0

不知怎的,既不是DB初始化的越来越调用。核心Identity 2.0数据库模式在应用程序运行时也未被创建。 –

+0

当您尝试注册帐户时会发生什么? –

+0

那么,我真正想要的是没有迁移的“原始”种子,但是因为我有迁移这样做。我做了添加迁移初始化,但是当我通过更新数据库关注它时,出现了一些错误,例如“EntityType'IdentityUserRole'没有定义密钥。” –

3

我想我可以解决谜题,为什么Seed永远不会被触发:因为Seed只在应用程序尝试连接数据库时调用,而不是在应用程序启动时调用。

我已经创建了一些示例,并且我已经成功地使用过您的代码,并且无需迁移。但是因为您想在启用迁移时使用它,下面是示例代码。

非常重要:要真正看到种子中的断点,运行该应用程序,按下Login并使用种子函数中的凭据来访问应用程序。如果你得到“无效的用户名或密码”,提防manager.PasswordValidator财产IdentityConfig.cs :: Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context).

  1. 在VS2013创建一个新的ASP.NET MVC 5项目

  2. 更新使用Update-Package命令所有包。

  3. 启用-迁移

  4. 在Configuration.cs通过迁移创建添加以下代码:

    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. 在Global.asax.cs中::的Application_Start()中,添加以下行:

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

  6. 运行!

如果您还需要禁用迁移的代码示例,请告诉我。

+0

我决定摆脱Migrations,因为它不适用于应用程序无权创建/删除数据库的共享虚拟主机。所以,我试图用非迁移方法来做种子。 –

+0

好的!你还需要一个可用的代码示例吗? –

+0

嗨Corneliu,如果你不介意我想看到一个例子,使这个与迁移禁用; o) –

2

所以,我们做我们的样品包装以下面的方式类似(种子与我们的管理员用户DB)

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

要完成这个问题。至于Corneliu Serediuc说,所有你需要做的仅仅是尝试在应用程序启动连接到数据库,例如,像这样(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; 
    } 

} 顺便说一句,科尔内留感谢您的代码示例,他们帮了我很多。