10

How do you seed users, roles and app specific entities? It appears as though the IdentityModel targets its own Context?

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

    protected override void Seed(Project.Models.SchoolContext context)
    {
        // Seed the Entities
        //    context.People.AddOrUpdate(
        //      p => p.FullName,
        //      new Person { FullName = "Andrew Peters" }            
        //    );
        //
    }
}

vs.

protected override void Seed(Project.Models.ApplicationDbContext context)
{
    if (!context.Roles.Any(r => r.Name == "AppAdmin"))
    {
        var store = new RoleStore<IdentityRole>(context);
        var manager = new RoleManager<IdentityRole>(store);
        var role = new IdentityRole { Name = "AppAdmin" };
        manager.Create(role);
    }

    if (!context.Users.Any(u => u.UserName == "founder"))
    {
        var store = new UserStore<ApplicationUser>(context);
        var manager = new UserManager<ApplicationUser>(store);
        var user = new ApplicationUser {UserName = "founder"};

        manager.Create(user, "ChangeItAsap!");
        manager.AddToRole(user.Id, "AppAdmin");
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
JReam
  • 898
  • 2
  • 13
  • 28

1 Answers1

12

I don't seed from the migration, instead use the context db initializer. My context derives from IdentityDbContext so I use this method to seed users and roles:

Call an initializer from ctor:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    private readonly IHttpContextBaseWrapper _httpContextBaseWrapper;

    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(new ApplicationDbInitializer());
    }
...

Then my seed code:

public class ApplicationDbInitializer : CreateDatabaseIfNotExists<ApplicationDbContext>
{
    protected override void Seed(ApplicationDbContext context)
    {
        InitializeIdentityForEF(context);
        base.Seed(context);
    }

    public static void InitializeIdentityForEF(ApplicationDbContext db)
    {

        if (!db.Users.Any())
        {
            var roleStore = new RoleStore<IdentityRole>(db);
            var roleManager = new RoleManager<IdentityRole>(roleStore);
            var userStore = new UserStore<ApplicationUser>(db);
            var userManager = new UserManager<ApplicationUser>(userStore);

            // Add missing roles
            var role = roleManager.FindByName("Admin");
            if (role == null)
            {
                role = new IdentityRole("Admin");
                roleManager.Create(role);
            }

            // Create test users
            var user = userManager.FindByName("admin");
            if (user == null)
            {
                var newUser = new ApplicationUser()
                {
                    UserName = "admin",
                    FirstName = "Admin",
                    LastName = "User",
                    Email = "xxx@xxx.net",
                    PhoneNumber = "5551234567",
                    MustChangePassword = false
                };
                userManager.Create(newUser, "Password1");
                userManager.SetLockoutEnabled(newUser.Id, false);
                userManager.AddToRole(newUser.Id, "Admin");
            }
...
InteXX
  • 6,135
  • 6
  • 43
  • 80
Steve Greene
  • 12,029
  • 1
  • 33
  • 54
  • 1
    looks good, thanks! is there a way to target both the default db for users/roles etc and a new dbcontext for entities to seed both with one initilizer? It seems as though EF sets up its own DbContext for account management/ Identity whereas we as the developers target a different DbContext for crud operations on entities/models? Am I understanding this correctly? – JReam Apr 12 '15 at 13:55
  • 1
    We set ours up to derive from IdentityDbContext so we only have 1 initializer, migration, etc for both identity and business classes. For a larger architecture it may limit your ability to separate the data stuff into a different project or repository, but it works well for us. Julie Lerman has a couple of good articles on the topic. https://msdn.microsoft.com/en-us/magazine/jj883952.aspx – Steve Greene Apr 12 '15 at 16:15
  • 1
    Thanks, Steve. So in the above example; after your if (!db.Users.Any()){}, you would start creating new objects for the business classes? – JReam Apr 13 '15 at 15:19
  • 1
    You could call a different routine from Seed() like InitializeBusinessObjects(context); for things like lookup tables, etc. You could also put that code in the migration configuration Seed() method. – Steve Greene Apr 13 '15 at 15:58
  • 2
    Any idea how to do it in EF Core? – Tohid Mar 11 '19 at 11:45
  • How are you getting that `Seed()` method to run? – InteXX Sep 23 '20 at 04:20
  • Initializer seed only runs on database creation. You could switch to a model change initializer if needed. – Steve Greene Sep 23 '20 at 22:17
  • *"Initializer seed only runs on database creation"* Right. Got that. Trouble is, it just doesn't run. For me, at least. – InteXX Sep 28 '20 at 16:47