База данных Seeding Identity 2.0
У меня есть проект ASP.NET MVC 5 (движок бритвы), который имеет Identity 2.0 с отдельными учетными записями пользователей. Я использую Visual Studio Professional 2013
Я не нашел никакого ясного примера (почему он не выходит из коробки?) КАК Я могу засеять базу данных Identity 2.0, и все примеры, которые я вижу, наполовину поддержаны, потому что они не говорят, ГДЕ для реализации этого.
Я использовал enable-migrations, который создал папку Migrations с файлом Configuration.cs. Он имеет метод Seed, но когда я помещаю точку останова, он замечает, что он никогда не выполняется, на самом деле база данных Identity даже не заполнена схемой.
Итак, где и что мне нужно сделать, чтобы схема Identity 2.0 была создана в базе данных в первый раз (строка подключения верна и существует пустая база данных). И как мне настроить сеялку?
В IdentityModels.cs У меня есть следующее: public class ApplicationDbContext: IdentityDbContext { public 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");
}
}
В Migrations/Configuration.cs(добавлено PM > Enable-Migrations) У меня есть следующее:
internal sealed class Configuration : DbMigrationsConfiguration<Models.ApplicationDbContext> {
public Configuration() {
AutomaticMigrationsEnabled = false;
}
protected override void Seed(Models.ApplicationDbContext context) {
WriteReferenceData();
}
}
В моем файле Global.asax.cs в методе Application_Start() я добавил следующее:
System.Data.Entity.Database.SetInitializer<Models.ApplicationDbContext>(new System.Data.Entity.MigrateDatabaseToLatestVersion<Models.ApplicationDbContext, Migrations.Configuration>());
И в IdentityConfig.cs У меня есть этот инициализатор базы данных, хотя он кажется сиротой, потому что я не знаю, где его включить:
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;
Ответы
Ответ 1
EF имеет два разных метода Seed. Один, который используется с инициализаторами базы данных, и другой, который используется с Migrations. Поскольку вы включили Migrations, я опишу, как это сделать с помощью метода Migrations Seed здесь...
Прежде всего, хотя вы включили Migrations, по умолчанию EF по-прежнему использует инициализатор базы данных CreateDatabaseIfNotExists
. Это означает, что при запуске приложения при первом обращении к ApplicationDbContext
вызывается инициализатор и он создает таблицы базы данных из сопоставлений кода первого, если эти таблицы уже не существуют. Вы не видите схему, созданную, потому что вы, вероятно, не получили доступ к контексту db. В новом веб-приложении это обычно срабатывает при первом входе в систему пользователя или попытке входа в систему.
Чтобы засеять таблицы ASP.NET Identity, вам нужно сделать две вещи. Первый заключается в добавлении семенной логики к методу Seed в Configuration.cs
. Второй способ - запустить 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
из консоли диспетчера пакетов, чтобы перенести базу данных на последнюю схему и запустить метод Seed.
Или вы можете изменить EF, чтобы использовать инициализатор MigrateDatabaseToLatestVersion
, изменив контекст в IdentityModels.cs
...
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
Database.SetInitializer<ApplicationDbContext>(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
}
}
Теперь, когда вы запускаете приложение, при первом использовании контекста базы данных он запускает update-database
и заносит его.
Ответ 2
Я думаю, что могу решить загадку как ПОЧЕМУ семя никогда не запускается: потому что семя вызывается только тогда, когда приложение пытается подключиться к базе данных, а НЕ, когда приложение запускается.
Я создал примеры, и я успешно использовал ваш код с миграциями и без них. Но поскольку вы хотите использовать его с включенными переходами, ниже приведен пример кода.
ОЧЕНЬ ВАЖНО. Чтобы увидеть точку останова внутри Seed, запустите приложение, нажмите "Войти" и используйте учетные данные из функции seed, чтобы получить доступ к приложению. Если вы получили "Неверное имя пользователя или пароль", следите за свойством manager.PasswordValidator
в IdentityConfig.cs :: Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context).
-
Создайте новый проект ASP.NET MVC 5 в VS2013
-
Обновите все пакеты с помощью команды "Обновить пакет".
-
Enable-Миграции
-
В 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");
}
}
}
-
В Global.asax.cs:: Application_Start() добавьте следующую строку:
Database.SetInitializer<ApplicationDbContext>(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
-
Run!
Если вам также нужен образец кода с отключенными миграциями, дайте мне знать.
Ответ 3
Итак, мы делаем что-то подобное в нашем пакете образцов следующим образом (Чтобы засеять 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);
}
}
Ответ 4
Чтобы закончить этот вопрос.. Как 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;
}
}
Кстати, Корнелиу благодарит вас за примеры кода, они мне очень помогают.
Ответ 5
Если вы хотите, чтобы вставлять новые учетные записи в CSV, вы можете использовать этот проект: https://github.com/Stonefinch/AspNetUserMaintenanceAzureSiteExtension
Это также может быть установлено как расширение Azure Site: https://www.siteextensions.net/packages/AspNetUserMaintenanceAzureSiteExtension/