0

I've successfully implemented ApplicationUser and Roles into my MVC .NET Core app. However, when I'm returning this to the view it does not populate that a user has been added to a role.

Here is the controllers related part:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit (string id, ApplicationUser applicationUser)
{
    if (id != applicationUser.Id)
    {
        return NotFound();
    }
    if (ModelState.IsValid)
    {
        ApplicationUser userFromDb = _db.ApplicationUsers.Where(u => u.Id == id).FirstOrDefault();

        userFromDb.FirstName = applicationUser.FirstName;
        userFromDb.LastName = applicationUser.LastName;

        if (applicationUser.IsModerator)
        {
            await _userManager.AddToRoleAsync(userFromDb, SD.ModeratorEndUser);
        }
        if (!applicationUser.IsModerator)
        {
            await _userManager.RemoveFromRoleAsync(userFromDb, SD.ModeratorEndUser);
        }

        if (applicationUser.IsOwner)
        {
            await _userManager.AddToRoleAsync(userFromDb, SD.OwnerEndUser);
        }
        if (!applicationUser.IsOwner)
        {
            await _userManager.RemoveFromRoleAsync(userFromDb, SD.OwnerEndUser);
        }

        _db.SaveChanges();
        return RedirectToAction(nameof(Index));
    }

This controller is successfully adding or removing the role that is passed to it from the view, which I will show below: (When I check the SQL Table - it has added or removed successfully to the AspNetUserRoles table so the functionality is working - I just can't get it to display appropriately after this.)

@model Sentimented.Models.ApplicationUser

@{
    ViewData["Title"] = "Edit";
}
<br />
<h2 class="text-info">Edit User Roles</h2>

<form method="post" asp-action="Edit">
    <div class="p-4 border rounded">
        <input type="hidden" asp-for="Id" />
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group row">
            <div class="col-2">
                <label asp-for="FirstName"></label>
            </div>
            <div class="col-5">
                <input asp-for="FirstName" class="form-control" />
            </div>
            <span asp-validation-for="FirstName" class="text-danger"></span>
        </div>
        <div class="form-group row">
            <div class="col-2">
                <label asp-for="LastName"></label>
            </div>
            <div class="col-5">
                <input asp-for="LastName" class="form-control" />
            </div>
            <span asp-validation-for="LastName" class="text-danger"></span>
        </div>
        <div class="form-group">
            <label asp-for="IsModerator"></label>
            <input asp-for="IsModerator" type="checkbox" class="form-control" />
        </div>
        <div class="form-group">
            <label asp-for="IsOwner"></label>
            <input asp-for="IsOwner" type="checkbox" class="form-control" />
        </div>
        <br />
        <div class="form-group">
            <input type="submit" class="btn btn-primary" asp-route-id="@Model.Id " value="Update" />
            <a asp-action="Index" class="btn btn-success">Back to List</a>
        </div>
    </div>
</form>

@section Scripts{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

And here the Index view which is incorrectly showing the Roles as false.

      @model IEnumerable<Sentimented.Models.ApplicationUser>

@{
    ViewData["Title"] = "Index";
}
<br />

<div class="row">
    <div class="col-6">
        <h2 class="text-info">User List</h2>
    </div>

    <div class="col-6 text-right">
        <a asp-action="New" class="btn btn-info"><i class="fas fa-plus"></i>&nbsp; New Text</a>
    </div>
</div>

<br />

<div>
    <table class="table table-striped border">
        <tr class="table-info">
            <th>
                @Html.DisplayNameFor(m => m.FirstName)
            </th>
            <th>
                @Html.DisplayNameFor(m => m.LastName)
            </th>
            <th>
                @Html.DisplayNameFor(m => m.Email)
            </th>
            <th>
                @Html.DisplayNameFor(m => m.IsOwner)
            </th>
            <th>
                @Html.DisplayNameFor(m => m.IsModerator)
            </th>
            <th>
                Edit
            </th>
            <th>
                Details
            </th>
        </tr>

        @foreach (var item in Model)
        {
    <tr>
        <td>@Html.DisplayFor(m => item.FirstName)</td>
        <td>@Html.DisplayFor(m => item.LastName)</td>
        <td>@Html.DisplayFor(m => item.Email)</td>
        <td>@Html.DisplayFor(m => item.IsModerator)</td>
        <td>@Html.DisplayFor(m => item.IsOwner)</td>

        <td>
            <a type="button" class="btn btn-primary" href="@Url.Action("Edit/" + item.Id)">
                <i class="fas fa-edit"></i>
            </a>
        </td>
        <td>
            <a type="button" class="btn btn-primary" href="@Url.Action("Details/" + item.Id)">
                <i class="fas fa-edit"></i>
            </a>
        </td>
    </tr>
        }
    </table>
</div>

Apologies if I've included too much code - but how do I get the above view to return the item.IsModerator and item.IsOwner as true and not false?

Thank you!

Edit:

See Below for these users index. Both of these users are assigned to role Owner and Moderator via the edit button (which is the first controller posted above). But On returning to the Index page it does not show them as active.

Index of User Page

And here is the related Index Controller:

public IActionResult Index()
{
    var users = _db.ApplicationUsers.ToList();

    return View(users);
}
smithereens
  • 56
  • 10

1 Answers1

0

Since the user is already logged in, this line of code await _userManager.AddToRoleAsync(userFromDb, SD.OwnerEndUser); will update the db , but not the cookie. You have to have to re-issue the cookie :

So after _db.SaveChanges(); , you can use _signInManager.RefreshSignInAsync(userFromDb).

You need to inject SignInManager like :

private readonly SignInManager<ApplicationUser> _signInManager;
public HomeController(SignInManager<ApplicationUser> signInManager)
{
    _signInManager = signInManager;
}

And refresh the user's cookie :

await _signInManager.RefreshSignInAsync(userFromDb);
Nan Yu
  • 21,285
  • 5
  • 39
  • 110
  • In my Index, I'm showing a list of all users, and their associated roles (which is the portion that is not working correctly). It is correctly adding the role but is not specific to the user that is logged in so I don't think that cookies are causing this issue as it is hitting the DB when I reload the page index page? – smithereens Jul 08 '19 at 03:31
  • So before rending the index page , do you re-query the users/roles on server side ? – Nan Yu Jul 08 '19 at 05:14
  • Yes - I have posted the index controller in an edit above which hopefully makes it more clear. I just somehow need to also pass the roles into the index view for each user I am just not sure what I am doing wrong. – smithereens Jul 08 '19 at 12:29
  • You *also* need a redirect. If you reissue the cookie, but just return the view directly, the new cookie will be set, but won't be sent back by the client, which means the server is still operating on the old version. A redirect will ensure that the new cookie that was set is sent back to the server and that that is what the server is seeing. – Chris Pratt Jul 08 '19 at 14:20
  • I don't believe a cookie refresh is what I need. I've deleted browser history, restart the application and it still is not showing the role as added even though I have confirmed it in the database. I guess I just need to figure out to return the ASPNetUserRoles table to a view in another way. – smithereens Jul 08 '19 at 22:50
  • So that your problem is not getting role information about each user , check [this thread](https://stackoverflow.com/questions/51004516/net-core-2-1-identity-get-all-users-with-their-associated-roles) for code sample/solution . – Nan Yu Jul 10 '19 at 03:25