MVC 5 Seed Users and Roles
Asked Answered
G

7

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?

Giffin answered 9/10, 2013 at 19:8 Comment(1)
#19214705Issue
M
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.

Mirabel answered 11/12, 2013 at 14:18 Comment(17)
To the Seed method of Configuration class. Configuration is default class name for enable-migrations, but you could change it.Mirabel
I have no seed method. Only two classes in the model folder?Viral
It's in Migrations folder. Are you using migrations for your database context?Mirabel
Oh I see. I have not used migrations yet. Its just the plain default mvc project. Do I have to use migrations for seeds?Viral
You should use 'enable-migrations'in package manager console. It will create configuration class with seed method for you.Mirabel
@Viral 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.Genetics
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?Pedantry
Seed is not an asynchronous method. You use it only to initialize data.Mirabel
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.Quotha
The role logic requires using Microsoft.AspNet.Identity and Microsoft.AspNet.Identity.EntityFramework.Sheathbill
I can't login with the founder login How is that possible?Holston
@TomRegan check this answer, TL;DR : maybe user create failedBroccoli
The Configuration and Seed methods appear to have been depreciated in later versions. Can anyone answer where you'd do this now?Condition
Missed context.Users.Add(user); between manager.Create(user, "ChangeItAsap!"); and manager.AddToRole(user.Id, "AppAdmin");. So newborn user have not User.Id.Helminthiasis
@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.Proustite
This answer no longer seems to work in newer versions, as you cannot just instantiate the UserManager and RoleManager anymore.Penetrance
@KoenVanLooveren because login page uses Email instead of Username. set username same as the email to fix the login problem.Eyeleen
N
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.

Nickname answered 11/6, 2015 at 19:36 Comment(3)
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()); }Discant
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 userIdHomager
I found my password validation rule not work too, any idea?Broccoli
R
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 == "[email protected]"))
    {
        var user = new ApplicationUser
        {
             UserName = "[email protected]",
             Email = "[email protected]",
             PasswordHash = PasswordHash.HashPassword("123456")
         };

         UserManager.Create(user);
         UserManager.AddToRole(user.Id, Const.getRoles()[0]);
    }
Riley answered 6/7, 2015 at 11:54 Comment(0)
F
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);
    }
Foliaceous answered 4/10, 2017 at 22:53 Comment(2)
Cool thing, but u missed an important thing. You have to add user.SecurityStamp = Guid.NewGuid().ToString() or you will get an error on login.Sean
Thx. I did not use that feature but I have added it to my answer.Foliaceous
C
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 = "[email protected]",
      EmailConfirmed = true,
      PhoneNumber = "0123456789",
      PhoneNumberConfirmed = true
    };

    await userManager.CreateAsync(user, "****");
    await userManager.AddToRoleAsync(user.Id, ApplicationRole.AdminRoleName);
  }
}
Cupid answered 1/11, 2015 at 6:55 Comment(2)
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!Subtract
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.Aikoail
G
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();
        }
    }
}
Giffin answered 9/10, 2013 at 21:47 Comment(2)
This answer is no longer relevant as the ASP.NET Identity API has changed.Hellkite
@Josh McKearin Do you have a better solution? please shareThorium
C
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" }
            );


    }
}
Chilson answered 12/4, 2019 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.