Understanding MVC-5 Identity

2019-03-14 06:26发布

问题:

I created a new ASP.NET MVC-5 application with Individual User Accounts and then updated all the Nuget packages in the solution. Now I'm trying to follow some of the guidelines shown in some tutorials but I encountered some problems. The first one is that a class called ApplicationRoleManager which is being used throughout the application wasn't created (the ApplicationUserManager was created). The second problem is more about Entity-Framework: I've seen that for seeding the database with a user and role many people create a static constructor in the ApplicationDbContext class:

    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }

So I added it, and the implementation of the ApplicationDbInitializer is:

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

    //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role
    public static void InitializeIdentityForEF(ApplicationDbContext db)
    {
        var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
        const string name = "admin@admin.com";
        const string password = "Admin@123456";
        const string roleName = "Admin";

        //Create Role Admin if it does not exist
        var role = roleManager.FindByName(roleName);
        if (role == null)
        {
            role = new IdentityRole(roleName);
            var roleresult = roleManager.Create(role);
        }

        var user = userManager.FindByName(name);
        if (user == null)
        {
            user = new ApplicationUser { UserName = name, Email = name };
            var result = userManager.Create(user, password);
            result = userManager.SetLockoutEnabled(user.Id, false);
        }

        // Add user admin to Role Admin if not already added
        var rolesForUser = userManager.GetRoles(user.Id);
        if (!rolesForUser.Contains(role.Name))
        {
            var result = userManager.AddToRole(user.Id, role.Name);
        }
    }

After adding everything I opened the Package Manager Console and typed Enable-Migrations, then Add-Migration someName and then Update-Database. the results were that the database was created successfully but no data was inserted to the database.
After noticing the data wasn't inserted I moved the Seed logic to the Index method of the home controller and the data was inserted after running the application. I also needed to add this line: app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create); to the Startup.Auth.cs file.
So my questions are:

  1. Do I really need to enter the ApplicationRoleManager class manually?
  2. How do I make the seed method work?

UPDATE
I've changed the Seed method to:

protected override void Seed(ApplicationDbContext context)
    {
        var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();

        //since there is no ApplicationRoleManager (why is that?) this is how i create it
        var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));

        const string name = "admin@admin.com";
        const string password = "Admin@123456";
        const string roleName = "Admin";

        //Create Role Admin if it does not exist
        var role = roleManager.FindByName(roleName);
        if (role == null)
        {
            role = new IdentityRole(roleName);
            var roleresult = roleManager.Create(role);
        }
        //app hangs here...
        var user = userManager.FindByName(name);
        if (user == null)
        {
            user = new ApplicationUser { UserName = name, Email = name };
            var result = userManager.Create(user, password);
            result = userManager.SetLockoutEnabled(user.Id, false);
        }

        // Add user admin to Role Admin if not already added
        var rolesForUser = userManager.GetRoles(user.Id);
        if (!rolesForUser.Contains(role.Name))
        {
            var result = userManager.AddToRole(user.Id, role.Name);
        }
        base.Seed(context);
    }

So now, the Admin role is created but when getting to var user = userManager.FindByName(name); the application hangs with no exception or any message...

回答1:

When using migrations you can use the built in initializer and the Seed method:

Database.SetInitializer<ApplicationDbContext>(new 
    MigrateDatabaseToLatestVersion<ApplicationDbContext, 
    APPLICATION.Migrations.Configuration>());

and in APPLICATION.Migrations.Configuration (this was created by the Enable-Migrations command):

protected override void Seed(ApplicationDbContext context)
{
    // seed logic
}

As a role manager you can also use the RoleManager<ApplicationRole> base implementation.



回答2:

I also was a bit confused about hanging of application in this case. The problem can be solved in this way

var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUserManager>(db));
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(db));


回答3:

And for anyone using the ApplicationUser with Integer foreign key, the code is this one:

var userManager = new ApplicationUserManager(new ApplicationUserStore(context));
var roleManager = new ApplicationRoleManager(new ApplicationRoleStore(context));


回答4:

This works great for default MVC 5 project.

var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context));


回答5:

It doesn't appear that the solutions posted address the issue of the app hanging on the call of userManager.FindByName(name). I'm running into the same problem. It worked a few hours ago on my local. I published to Azure and it started hanging. When I tested my local again it all of a sudden started hanging at that step. No error is returned and no timeout (at least after waiting 10-15 minutes). Does anyone have any tips to address Yoav's ultimate question?

I have some other very simple seeding processes that run before adding roles, and db.Foo.AddOrUpdate(foo) calls are running without error, but not actually saving anything to the database.



回答6:

I just spent a deeply unpleasant half day dealing with this. I finally managed to get the damn thing to fire:

public static void InitializeIdentityForEF(ApplicationDbContext context)
        {
            context.Configuration.LazyLoadingEnabled = true;

            //var userManager = HttpContext.Current
            //    .GetOwinContext().GetUserManager<ApplicationUserManager>();

            //var roleManager = HttpContext.Current
            //    .GetOwinContext().Get<ApplicationRoleManager>();

            var roleStore = new RoleStore<ApplicationRole, int, ApplicationUserRole>(context);
            var roleManager = new RoleManager<ApplicationRole, int>(roleStore);
            var userStore = new UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context);
            var userManager = new UserManager<ApplicationUser, int>(userStore);   
...

It's the end of an extremely long day, and I suspect someone's going to tell me why I shouldn't do this. The rest of my Seed method fires beautifully, however, using non-async methods (FindByName/Create).



回答7:

Sir goobering, You struggles have helped me get passed this problem, I had to do it a little different though.

   context.Configuration.LazyLoadingEnabled = true;

        //var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
        //var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();

        const string name = "admin@example.com";
        const string password = "Admin@123456";
        const string roleName = "Admin";

        ***var userManager = new ApplicationUserManager(new UserStore<ApplicationUser>(context));
        var roleManager = new ApplicationRoleManager(new RoleStore<IdentityRole>(context));***

        //Create Role Admin if it does not exist
        var role = roleManager.FindByName(roleName);
        if (role == null) {
            role = new IdentityRole(roleName);
            var roleresult = roleManager.Create(role);
        }