98

I have been playing about with the new MVC 5, I have a few models, controller and views setup using code first migrations.

My question is how do I seed users and roles? I currently seed some reference data in my Seed method in Configuration.cs. But it looks to me that the user and roles tables are not created until something first hits the AccountController.

I currently have two connection strings so I can separate my data from my authentication into different databases.

How can I get the user, roles, etc tables populate along with my others? And not when the account controller is hit?

Bryan Ash
  • 4,385
  • 3
  • 41
  • 57
MrBeanzy
  • 2,286
  • 3
  • 28
  • 38
  • http://stackoverflow.com/questions/19214205/websecurity-initializedatabaseconnection-doesnt-cooperate-with-code-first-migra/19218438#19218438 – Sirwan Afifi Oct 09 '13 at 19:51

7 Answers7

184

Here is example of usual Seed approach:

protected override void Seed(SecurityModule.DataContexts.IdentityDb 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");
    }
}

I used package-manager "update-database". DB and all tables were created and seeded with data.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Valin
  • 2,285
  • 1
  • 15
  • 10
  • 4
    To the Seed method of Configuration class. Configuration is default class name for enable-migrations, but you could change it. – Valin Jan 28 '14 at 13:25
  • I have no seed method. Only two classes in the model folder? – Zapnologica Jan 28 '14 at 13:27
  • It's in Migrations folder. Are you using migrations for your database context? – Valin Jan 28 '14 at 13:28
  • Oh I see. I have not used migrations yet. Its just the plain default mvc project. Do I have to use migrations for seeds? – Zapnologica Jan 28 '14 at 13:32
  • 3
    You should use 'enable-migrations'in package manager console. It will create configuration class with seed method for you. – Valin Jan 28 '14 at 16:31
  • 4
    @Zapnologica Migrations is so easy to use. It also allows you to edit your tables without re-creating your tables. There is only three commands you need to familiarize yourself with using the NuGet Package Manager Console. Enable-Migrations, Add-Migration, & update-database. Easy peazy. – yardpenalty.com Mar 06 '14 at 09:57
  • 1
    Don't you need to use async versions of these methods in MVC5 i.e. CreateAsync and AddToRoleAsync? Or am I confusing UserStore and UserManager? – Brendan Oct 01 '14 at 19:18
  • Seed is not an asynchronous method. You use it only to initialize data. – Valin Oct 02 '14 at 15:19
  • 11
    I literally copied and pasted this code into my Seed method in a new mvc 5 web application, and then ran "update-database" in the package manager console. It adds the role (I can see it in AspNetRoles table), but when it comes to the line manager.AddToRole(user.Id, "AppAdmin") I get the error message "UserId not found." If you have any idea what I'm missing I'd much appreciate the information. – Tom Regan Feb 09 '15 at 01:36
  • 1
    The role logic requires using Microsoft.AspNet.Identity and Microsoft.AspNet.Identity.EntityFramework. – TTT Jan 27 '16 at 17:11
  • I can't login with the founder login How is that possible? – vanlooverenkoen May 09 '16 at 13:40
  • 1
    @TomRegan check [this answer](http://stackoverflow.com/a/30789901/1686407), TL;DR : maybe user create failed – min Jul 06 '16 at 08:33
  • 1
    The Configuration and Seed methods appear to have been depreciated in later versions. Can anyone answer where you'd do this now? – mattpm Jan 18 '17 at 00:28
  • 2
    Missed `context.Users.Add(user);` between `manager.Create(user, "ChangeItAsap!");` and `manager.AddToRole(user.Id, "AppAdmin");`. So newborn user have not User.Id. – ApceH Hypocrite Feb 03 '17 at 10:16
  • @ApceHHypocrite In some cases: `context.Users.AddOrUpdate(user)`; Else you will have _Saving or accepting changes failed because more than one entity of type 'ApplicationUser' have the same primary key value._ – MSD561 May 30 '17 at 16:02
  • 3
    This answer no longer seems to work in newer versions, as you cannot just instantiate the UserManager and RoleManager anymore. –  Oct 05 '17 at 11:04
  • 1
    @KoenVanLooveren because login page uses `Email` instead of `Username`. set username same as the email to fix the login problem. – Jalal Jul 03 '18 at 19:36
16

It's a small addition, but to anyone having the "UserId not found." message when trying to seed: (Tom Regan had this question in the comments, and I was stuck on it myself for a while)

This means that the manager.Create(user, "ChangeItAsap!") was not successful. This might have a different reason, but for me it was because my password was not succeeding its validation.

I had a custom passwordvalidator, which was not being called when seeding the database, so the validation rules i was used to (minlength 4 instead of default 6) did not apply. Make sure your password (and all other fields for that matter) is passing validation.

Kevin
  • 1,516
  • 1
  • 16
  • 31
  • 7
    This helped me as I was having the "UserId not found" issue. I managed to track it down with the following code: `IdentityResult result = manager.Create(user, "ChangeItAsap!"); if (result.Succeeded == false) { throw new Exception(result.Errors.First()); }` – Steve Wilford Jun 25 '15 at 08:42
  • 1
    That comment is excellent, it gave me 'User name Demo User is invalid, can only contain letters or digits.' instead of just failing ambiguously with a missing userId – dougajmcdonald Nov 19 '15 at 21:45
  • I found my password validation rule not work too, any idea? – min Jul 06 '16 at 10:21
15

This is my method base on Valin answer, I have added roles in db and added password for user. This code is placed in Seed() method in Migrations>Configurations.cs.

// role (Const.getRoles() return string[] whit all roles)

    var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
    for (int i = 0; i < Const.getRoles().Length; i++)
    {
        if (RoleManager.RoleExists(Const.getRoles()[i]) == false)
        {
            RoleManager.Create(new IdentityRole(Const.getRoles()[i]));
        }
    }

// user

    var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
    var PasswordHash = new PasswordHasher();
    if (!context.Users.Any(u => u.UserName == "admin@admin.net"))
    {
        var user = new ApplicationUser
        {
             UserName = "admin@admin.net",
             Email = "admin@admin.net",
             PasswordHash = PasswordHash.HashPassword("123456")
         };

         UserManager.Create(user);
         UserManager.AddToRole(user.Id, Const.getRoles()[0]);
    }
Eitan K
  • 837
  • 1
  • 17
  • 39
Vasil Valchev
  • 5,701
  • 2
  • 34
  • 39
6

Here i have an very easy,clean and smooth solution.

 protected override void Seed(UserContext context)
    { 
        //Step 1 Create the user.
        var passwordHasher = new PasswordHasher();
        var user = new IdentityUser("Administrator");
        user.PasswordHash = passwordHasher.HashPassword("Admin12345");
        user.SecurityStamp = Guid.NewGuid().ToString();

        //Step 2 Create and add the new Role.
        var roleToChoose = new IdentityRole("Admin");
        context.Roles.Add(roleToChoose);

        //Step 3 Create a role for a user
        var role = new IdentityUserRole();
        role.RoleId = roleToChoose.Id;
        role.UserId = user.Id;

         //Step 4 Add the role row and add the user to DB)
        user.Roles.Add(role);
        context.Users.Add(user);
    }
Mr Tangjai
  • 153
  • 2
  • 9
4
protected override void Seed(ApplicationDbContext context)
{
  SeedAsync(context).GetAwaiter().GetResult();
}

private async Task SeedAsync(ApplicationDbContext context)
{
  var userManager = new ApplicationUserManager(new UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context));
  var roleManager = new ApplicationRoleManager(new RoleStore<ApplicationRole, int, ApplicationUserRole>(context));

  if (!roleManager.Roles.Any())
  {
    await roleManager.CreateAsync(new ApplicationRole { Name = ApplicationRole.AdminRoleName });
    await roleManager.CreateAsync(new ApplicationRole { Name = ApplicationRole.AffiliateRoleName });
  }

  if (!userManager.Users.Any(u => u.UserName == "shimmy"))
  {
    var user = new ApplicationUser
    {
      UserName = "shimmy",
      Email = "shimmy@gmail.com",
      EmailConfirmed = true,
      PhoneNumber = "0123456789",
      PhoneNumberConfirmed = true
    };

    await userManager.CreateAsync(user, "****");
    await userManager.AddToRoleAsync(user.Id, ApplicationRole.AdminRoleName);
  }
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
  • 1
    I customized my ApplicationUser to have an int typed ID property. Your approach is the only one i could get to work with my custom User and RoleStores, thanks! – Mike Devenney May 13 '17 at 14:10
  • 1
    This bit is completely incorrect from a conceptual point of view: `Task.Run(async () => { await SeedAsync(context); }).Wait();`. You should rather write `SeedAsync(context).GetAwait().GetResult();` which is marginally better. – Tanveer Badar May 04 '20 at 15:06
2

Looks like they changes the way authentication works in MVC5, changed my Global.asax.cs to the following did the trick!

using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

using System.Threading.Tasks;
using MvcAuth.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System.Threading;
using Microsoft.AspNet.Identity.EntityFramework;

namespace MvcAuth
{
    public class MvcApplication : System.Web.HttpApplication
    {
        async Task<bool> AddRoleAndUser()
        {
            AuthenticationIdentityManager IdentityManager = new AuthenticationIdentityManager(
                new IdentityStore(new ApplicationDbContext()));

            var role = new Role("Role1");
            IdentityResult result = await IdentityManager.Roles.CreateRoleAsync(role, CancellationToken.None);
            if (result.Success == false)
                return false;

            var user = new ApplicationUser() { UserName = "user1" };
            result = await IdentityManager.Users.CreateLocalUserAsync(user, "Password1");
            if (result.Success == false)
                return false;

            result = await IdentityManager.Roles.AddUserToRoleAsync(user.Id, role.Id, CancellationToken.None);
            return result.Success;
        }

        protected async void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            bool x = await AddRoleAndUser();
        }
    }
}
MrBeanzy
  • 2,286
  • 3
  • 28
  • 38
2

write this code in your Migration Configuration.

note: Use ApplicationDbContext in Configuration Class.

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

    protected override void Seed(ApplicationDbContext context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data.
                   context.Roles.AddOrUpdate(p =>
            p.Id,
                new IdentityRole { Name = "Admins"},
                new IdentityRole { Name = "PowerUsers" },
                new IdentityRole { Name = "Users" },
                new IdentityRole { Name = "Anonymous" }
            );


    }
}
Farid Savad
  • 163
  • 1
  • 11