user1265215
user1265215

Reputation: 33

Trying to add users to roles in ASP.NET MVC. Changes not being saved

An application that I'm working on involves being able to add users to various roles. Right now I am able to create users and roles, as well as view the roles the users were assigned to in my seed data. However, when I try to add users to roles through the application itself, the changes don't actually save back to the database. I don't receive any kind of error page. Here is what I believe to be the relevant code.

The UserRole methods in my AccountController:

[Authorize(Roles = "Admin")]
    public ActionResult UserRoles(string id)
    {
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName == id);
        var model = new SelectUserRolesViewModel(user);
        return View(model);
    }

    [HttpPost]
    [Authorize(Roles = "Admin")]
    [ValidateAntiForgeryToken]
    public ActionResult UserRoles(SelectUserRolesViewModel model)
    {
        if (ModelState.IsValid)
        {
            var idManager = new IdentityManager();
            var Db = new ApplicationDbContext();
            var user = Db.Users.First(u => u.UserName == model.UserName);
            idManager.ClearUserRoles(user.Id);
            foreach (var role in model.Roles)
            {
                if (role.Selected)
                {
                    idManager.AddUserToRole(user.Id, role.RoleName);
                }
            }
            return RedirectToAction("IndexUser");
        }
        return View();
    }

Identity Manager Class:

public class IdentityManager
{
    public bool AddUserToRole(string userId, string roleName)
    {
        var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
        var idResult = um.AddToRole(userId, roleName);
        return idResult.Succeeded;
    }

    public void ClearUserRoles(string userId)
    {
        var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
        var user = um.FindById(userId);
        var currentRoles = new List<IdentityUserRole>();
        currentRoles.AddRange(user.Roles);
        foreach (var role in currentRoles)
        {
            um.RemoveFromRole(userId, role.RoleId);
        }
    }
}

Model Classes:

public class SelectUserRolesViewModel
{
    // Enable initialization with an instance of ApplicationUser:
    public SelectUserRolesViewModel(ApplicationUser user)
        : this()
    {
        this.UserName = user.UserName;

        var Db = new ApplicationDbContext();

        // Add all available roles to the list of EditorViewModels:
        var allRoles = Db.Roles;
        foreach (var role in allRoles)
        {
            var rvm = new SelectRoleEditorViewModel(role);
            this.Roles.Add(rvm);
        }

        // Set the Selected property to true for those roles for 
        // which the current user is a member:
        foreach (var userRole in user.Roles)
        {
            var checkUserRole = this.Roles.Find(r => r.RoleID == userRole.RoleId);
            checkUserRole.Selected = true;
        }
    }

    public string UserName { get; set; }
    public List<SelectRoleEditorViewModel> Roles { get; set; }
}

// Used to display a single role with a checkbox, within a list structure:
public class SelectRoleEditorViewModel
{
    public SelectRoleEditorViewModel() { }

    public SelectRoleEditorViewModel(IdentityRole role)
    {
        this.RoleName = role.Name;
        this.RoleID = role.Id;
    }

    public bool Selected { get; set; }

    [Required]
    public string RoleName { get; set; }

    [Required]
    public string RoleID { get; set; }

}

Now I know the AddUserToRole method works, as I call it directly when I seed my database and it works. Anybody have any ideas?

For full disclosure, this code was taken from the following tutorial: http://www.codeproject.com/Articles/682113/Extending-Identity-Accounts-and-Implementing-Rol

EDIT: A little more description about what actually happens in the webpage. When I navigate to the page to assign the roles to the selected user, it lists all the roles and pre-checks all the roles the user is already assigned to. I can make some changes and then click the save button. However, at this time, it returns the same view however without any user role information on it. I can then click save again and it goes back to the user list page. So I guess what is happening is when page is first submitted when there model values attached (the user and the roles) the model state is always coming back invalid, hence it is bypassing all the code in the UserRoles method and going straight to the error handling return View() line at the end. However, I have no idea why the model would be invalid. There is nowhere for the user to enter values that could violate some sort of DB or webpage validation. There are simply checkboxes beside each role to denote which roles the user is to be assigned to.

Upvotes: 0

Views: 4187

Answers (4)

FWest98
FWest98

Reputation: 91

In your ClearUserRoles function, you have um.RemoveFromRole(userId, role.RoleId);. You give the RoleId where that should be the RoleName.

Upvotes: 1

Fafore Tunde
Fafore Tunde

Reputation: 319

I hope this helps someone who comes across this and needs answers. The problem you're having is that your model.roles is returning null so when you try adding a new user, it should give an error. However if you check for it not being empty, then it wouldn't give any error. Also, next time try adding an error message yourself, use try catch in a case where an operation can not be performed, you get an error message. Try below

if (model.Roles == null)
{
     model.Roles = new Role();
}
  else {
foreach (var role in model.Roles)
        {
            if (role.Selected)
            {
                idManager.AddUserToRole(user.Id, role.RoleName);
            }
        }
 }

Upvotes: 1

Pratik Bhoir
Pratik Bhoir

Reputation: 2144

in the Account controller you are not saving the changes to the database
Use Db.SaveChanges(); to save the changes

[HttpPost]
[Authorize(Roles = "Admin")]
[ValidateAntiForgeryToken]
public ActionResult UserRoles(SelectUserRolesViewModel model)
{
    if (ModelState.IsValid)
    {
        var idManager = new IdentityManager();
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName == model.UserName);
        idManager.ClearUserRoles(user.Id);
        foreach (var role in model.Roles)
        {
            if (role.Selected)
            {
                idManager.AddUserToRole(user.Id, role.RoleName);
            }
        }
        Db.SaveChanges(); //for saving changes to the database 
        return RedirectToAction("IndexUser");
    }
    return View();
}

Hope it may help you, Please tick the answer correct. :)

Upvotes: 1

Anthony Chu
Anthony Chu

Reputation: 37520

There are 3 DbContexts instantiated in the code and it looks like they might be nested, which can be problematic. Try changing the IdentityManager to use an external DbContext...

public class IdentityManager
{
    private ApplicationDbContext context;

    public IdentityManager ()
    {
        context = new ApplicationDbContext(); // if none supplied
    }

    public IdentityManager (ApplicationDbContext applicationDbContext)
    {
        context = applicationDbContext;
    }

    public bool AddUserToRole(string userId, string roleName)
    {
        var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
        var idResult = um.AddToRole(userId, roleName);
        return idResult.Succeeded;
    }

    public void ClearUserRoles(string userId)
    {
        var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
        var user = um.FindById(userId);
        var currentRoles = new List<IdentityUserRole>();
        currentRoles.AddRange(user.Roles);
        foreach (var role in currentRoles)
        {
            um.RemoveFromRole(userId, role.RoleId);
        }
    }
}

And change the calling code to pass in the DbContext...

var Db = new ApplicationDbContext();
var idManager = new IdentityManager(Db);
var user = Db.Users.First(u => u.UserName == model.UserName);
idManager.ClearUserRoles(user.Id);
foreach (var role in model.Roles)
{
    if (role.Selected)
    {
        idManager.AddUserToRole(user.Id, role.RoleName);
    }
}

Upvotes: 0

Related Questions