Search code examples
asp.netasp.net-core-mvcviewmodel

UserId field gets changed when passing from a view model to controller


I have a view for changing the role of a user on an admin page. I pass the userId to get the user information like the name. Users can be admins, employees, customers, or a company. There are three different kinds of companies a user can be if they are a company.

I have a view model for this webpage called RoleManagement.cshtml:

@model RoleManagementVM

<div class="card shadow border-0 mt-4">
    <div class="card-header bg-secondary bg-gradient ml-0 py-3">
        <div class="row">
            <div class="col-12 text-center">
                <h2 class="text-white py-2"> Manage User Role</h2>
            </div>
        </div>
    </div>
    <div class="card-body p-4">
        <form method="post" class="row">
            <input asp-formaction="ApplicationUser.Id" hidden />
            @Model.ApplicationUser.Id
            <div class="border p-3 ">


                <div class="form-floating py-2 col-12">
                    <input asp-for="ApplicationUser.Name" readonly class="form-control border-0 shadow" />
                    <label asp-for="ApplicationUser.Name" class="ms-2"></label>

                   
                </div>
                <div class="form-floating py-2 col-12">
                   <select asp-for="ApplicationUser.Role" asp-items="@Model.RolesList" class="form-select"></select>
                </div>
                @{
                    var companyVisible = "display:none;";  
                }
                @if (Model.ApplicationUser.Role == SD.Role_Company)
                {
                    companyVisible = "display:block;";
                }

                <div class="form-floating py-2 col-12">
                    <select asp-for="ApplicationUser.CompanyId" style="@companyVisible" asp-items="@Model.CompanyList" class="form-select"></select>
                </div>
                <div class="row pt-2">
                    <div class="col-6 col-md-3">
                        <button  type="submit" class="btn btn-primary form-control">Update Role</button>
                    </div>
                    <div class="col-6 col-md-3">
                        <a asp-action="Index" class="btn btn-primary border form-control">
                            Back to List
                        </a>
                    </div>
                </div>


            </div>
        </form>
    </div>
</div>


@section Scripts {
    @{
        <partial name="_ValidationScriptsPartial" />
    }
    <script>
        $(document).ready(function () {
            $('#ApplicationUser_Role').change(function () {
                var selection = $('#ApplicationUser_Role Option:Selected').text();
                if (selection == 'Company') {
                    $('#ApplicationUser_CompanyId').show();
                } else {
                    $('#ApplicationUser_CompanyId').hide();
                }

            })
        })
    </script>
}

My controller for this webpage is called UserController.cs. Here is the code:

using Ecommerce.DataAccess.Repository.IRepository;
using Ecommerce.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Ecommerce.Models.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Ecommerce.Utility;
using Ecommerce.DataAccess.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;



namespace Ecommerce.Areas.Admin.Controllers
{
    [Area("Admin")]
    [Authorize(Roles =SD.Role_Admin)]
    public class UserController : Controller
    {
        private readonly ApplicationDbContext _db;

       private readonly UserManager<IdentityUser> _userManager; 
        public UserController(ApplicationDbContext db, UserManager<IdentityUser> userManager)
        {
            _db = db;
            _userManager = userManager;
           
        }
        public IActionResult Index()
        {
           return View();
        }

        public IActionResult RoleManagment(string userId)
        {
            string RoleId = _db.UserRoles.FirstOrDefault(u=>u.UserId == userId).RoleId;
            RoleManagementVM RoleVM = new RoleManagementVM()
            {
                ApplicationUser = _db.ApplicationUsers.Include(u => u.Company).FirstOrDefault(u => u.Id == userId),
                RolesList = _db.Roles.Select(i => new SelectListItem
                {
                    Text = i.Name,
                    Value = i.Name
                }),
                CompanyList = _db.Company.Select(i => new SelectListItem {
                    Text = i.Name,
                    Value = i.Id.ToString()
                }),
            };
            RoleVM.ApplicationUser.Role = _db.Roles.FirstOrDefault(u => u.Id == RoleId).Name;
            return View(RoleVM);
        }

        [HttpPost]
        public IActionResult RoleManagment(RoleManagementVM roleManagementVM)
        {
            string RoleId = _db.UserRoles.FirstOrDefault(u => u.UserId == roleManagementVM.ApplicationUser.Id).RoleId;
            string oldRole = _db.Roles.FirstOrDefault(u => u.Id == RoleId).Name;
            
            if(!(roleManagementVM.ApplicationUser.Role == oldRole))
            {
                ApplicationUser applicationUser = _db.ApplicationUsers.FirstOrDefault(u => u.Id == roleManagementVM.ApplicationUser.Id);
                if(roleManagementVM.ApplicationUser.Role == SD.Role_Company)
                {
                    applicationUser.CompanyId = roleManagementVM.ApplicationUser.CompanyId;

                }
                if (oldRole == SD.Role_Company)
                {
                    applicationUser.CompanyId = null;
                }
                _db.SaveChanges();
                _userManager.RemoveFromRoleAsync(applicationUser, oldRole).GetAwaiter().GetResult();
                _userManager.AddToRoleAsync(applicationUser, roleManagementVM.ApplicationUser.Role).GetAwaiter().GetResult();
            }
            return RedirectToAction("Index");
        }



        //[HttpPost, ActionName("Delete")]
        //public IActionResult DeletePOST(int? id)
        //{
        //    Company obj = _db.ApplicationUsers.Include(u=>u.Company).ToList();
        //    if (obj == null)
        //    {
        //        return NotFound();
        //    }
        //    _unitOfWork.Company.Remove(obj);
        //    _unitOfWork.Save();
        //    TempData["success"] = "Company deleted successfully";
        //    return RedirectToAction("Index");



        //}
        #region API CALLS
        [HttpGet]
        public IActionResult GetAll()
        {
            List<ApplicationUser> obj = _db.ApplicationUsers.Include(u => u.Company).ToList();
            var userRoles = _db.UserRoles.ToList();
            var roles = _db.Roles.ToList();
            foreach(var user in obj)
            {

                var roleId = userRoles.FirstOrDefault(u => u.UserId == user.Id).RoleId;
                user.Role = roles.FirstOrDefault(u => u.Id == roleId).Name;
                if (user.Company == null)
                {
                    user.Company = new() { Name = "" };
                }
            }
            
            return Json(new { data = obj });
        }

        [HttpPost]
        public IActionResult LockUnlock([FromBody] string id)
        {
            var objFromDb = _db.ApplicationUsers.FirstOrDefault(u => u.Id == id);
            if (objFromDb == null)
            {
                return Json(new { success = false, message = "Error while Locking" });
            }
            if (objFromDb.LockoutEnd !=null && objFromDb.LockoutEnd > DateTime.Now)
            {
                objFromDb.LockoutEnd = DateTime.Now;
            }
            else
            {
                objFromDb.LockoutEnd = DateTime.Now.AddDays(30);
            }
            _db.SaveChanges();
            return Json(new { success = true, message = "Operation successful" });
        }

       
        #endregion
    }
}

When I try to change the role of a user, for example from company to employee, I get a an the following exception: Object reference not set to an instance of an object. This is due to the Id of the user being incorrect. In the view, you will notice that I am displaying the UserId. The correct userId is being displayed. When I debug to check the UserId being passed from the RoleManagementVM to the post action controller method, I get a completely different Id field (its not even in my db).

I am using ASP.NET 8. here is my project file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\Ecommerce.DataAccess\Ecommerce.DataAccess.csproj" />
    <ProjectReference Include="..\Ecommerce.Models\Ecommerce.Models.csproj" />
    <ProjectReference Include="..\Ecommerce.Utility\Ecommerce.Utility.csproj" />
  </ItemGroup>

  <ItemGroup>
    <Folder Include="wwwroot\images\product\" />
  </ItemGroup>

  <ItemGroup>
    <UpToDateCheckInput Remove="Areas\Customer\Views\Home\Details.cshtml" />
  </ItemGroup>

  <ItemGroup>
    <_ContentIncludedByDefault Remove="Areas\Customer\Views\Home\Details.cshtml" />
  </ItemGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="8.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-rc.2.23480.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" />
    <PackageReference Include="Stripe.net" Version="43.4.0" />
  </ItemGroup>

</Project>

I'm not sure if I'm doing something wrong or if there is a bug that is beyond what I can fix.


Solution

  • Try to change:

    <input asp-formaction="ApplicationUser.Id" hidden />
    

    into:

    <input asp-for="ApplicationUser.Id" hidden />