Search code examples
c#asp.net-mvcasp.net-identity

ASP.NET MVC + Identity implementation // Different ID returned with model than what is visible


Dear All,

This is my first post on Stackoverflow so if something is incorrect with the post please signal it so I could modify.

The issue

I have a view that provides visibility on the account information of a selected user based on a passed model that has a selected ApplicationUser as a member. When I make an HTTPpost request with a button every data from the model is passed but the ApplicationUser ID changes for some reason, therefore my HTTPPost action later on with FindById is not working correctly.

The model

public class ModifyAccountModel
{
    public ApplicationUser SelectedUser { get; set; }

    public IEnumerable<string> CurrentRoles { get; set; }

    public IEnumerable<IdentityRole> AvailableRoles { get; set; }
}

The view

@model Time_Window_Management_MVC.Models.ModifyAccountModel
@{
    ViewBag.Title = "Modify Account";
}

<h2>@ViewBag.Title</h2>

@using (Html.BeginForm("ModifyAccount", "Admin", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <h4>Attributes</h4>
    <div>
        @ViewBag.Errormessage
    </div>
    @Html.ValidationSummary("", new { @class = "text-danger" })
    <div class="form-group">
        <div class="col-md-2">
            User ID
        </div>
        @*@Html.LabelFor(m => m.FirstName, new { @class = "col-md-2 control-label" })*@
        <div class="col-md-10">
            @Html.DisplayFor(m => m.SelectedUser.Id)
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-2">
            First Name
        </div>
        @*@Html.LabelFor(m => m.FirstName, new { @class = "col-md-2 control-label" })*@
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.SelectedUser.FirstName, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-2">
            Second Name
        </div>
        @*@Html.LabelFor(m => m.SecondName, new { @class = "col-md-2 control-label" })*@
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.SelectedUser.SecondName, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-2">
            Sales Org.
        </div>
        @*@Html.LabelFor(m => m.SecondName, new { @class = "col-md-2 control-label" })*@
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.SelectedUser.SalesOrg, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-2">
            SAP System
        </div>
        @*@Html.LabelFor(m => m.SecondName, new { @class = "col-md-2 control-label" })*@
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.SelectedUser.SAP_System, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-2">
            Email
        </div>
        @*@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })*@
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.SelectedUser.Email, new { @class = "form-control" })
        </div>
    </div>
    <h4>Roles</h4>
    <table class="table">
        <tr>
            <td>
                Role name
            </td>
            <td>
                Actions
            </td>
        </tr>
        @if (Model.CurrentRoles.Count() == 0)
        {
            <tr>
                @Html.ActionLink("Add new role", "AddAccountRole", new { Id = Model.SelectedUser.Id })
            </tr>
        }
        else
        {
            foreach (var role in Model.CurrentRoles)
            {
                <tr>
                    <td>
                        @role
                    </td>
                    <td>
                        @Html.ActionLink("Delete", "DeleteAccountRole", new { Id = Model.SelectedUser.Id, Role = @role })
                    </td>
                </tr>
            }
            <tr>
                @Html.ActionLink("Add new role", "AddAccountRole", new { Id = Model.SelectedUser.Id })
            </tr>
        }
    </table>

    <div class="form-group">
        <div class="col-md-12">
            <input type="submit" class="btn btn-default" value="Save changes" />
        </div>
    </div>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

The action responding to post request

[HttpPost]
    public async Task<ActionResult> ModifyAccount(ModifyAccountModel model)
    {
        if (ModelState.IsValid)
        {
            ApplicationUser account = await UserManager.FindByIdAsync(model.SelectedUser.Id);

            if (account == null)
            {
                return RedirectToAction("ManageAccounts", "Admin", "Account modification couldn't be done.");
            }

            account.FirstName = model.SelectedUser.FirstName;
            account.SecondName = model.SelectedUser.SecondName;
            account.SalesOrg = model.SelectedUser.SalesOrg;
            account.SAP_System = model.SelectedUser.SAP_System;

            return RedirectToAction("ModifyAccount", "Admin", new { id = model.SelectedUser.Id });

        }

        return RedirectToAction("ManageAccounts", "Admin", "Something went wrong.");

    }

The only thing in the model that is changing is the ApplicationUser ID, everything stays as it should.

I know that as a workaround I could create an action that just sends the ID as string and then do the FindByIdAsync search based on that but would still like to know what is causing this issue.

I suspect that a new ApplicationUser is being created when posting with the same content as the original one and thus a new ID is created but I would like to have some clarification and probably some help on the matter.

Many thanks for any feedback, have a nice day!

Daniel


Solution

  • Good day, on this line

    @Html.DisplayFor(m => m.SelectedUser.Id)
    

    will only render int of current userId as an html string

    it will not be included in your post request then on your server the Binding will create new ApplicationUser with a new GUID every time. try simply changes it to

    @Html.TextBoxFor(m => m.SelectedUser.Id)
    

    that will produces html input (with Name attribute)