База данных 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;
    }

} Кстати, Корнелиу благодарит вас за примеры кода, они мне очень помогают.