Search code examples
c#asp.net-coreasp.net-identity.net-6.0razor-pages

How do I use asp.net-Identity to create a role and assign it to a user?


I'm trying to copy the cs code used within the Identity scaffold Pages (like Register.cshtml.cs) but to be used in the context of Roles and RoleManager. I simply want to create a page which creates an "Administrator" role and assigns it to the active logged in user with this page code that executes OnGet():



namespace Scratch.Pages
{
    public class AdminUserModel : PageModel
    {

        
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IUserStore<IdentityUser> _userStore;

        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly IRoleStore<IdentityRole> _roleStore;

        

        private readonly ILogger<AdminUserModel> _logger;

        public AdminUserModel(ILogger<AdminUserModel> logger,
            UserManager<IdentityUser> userManager,
            IUserStore<IdentityUser> userStore,
            RoleManager<IdentityRole> roleManager,
            IRoleStore<IdentityRole> roleStore)
        {
            _logger = logger;
            _userManager = userManager;
            _userStore = userStore;
            _roleManager = roleManager;
            _roleStore = roleStore;
        }

        public async void OnGet(bool setAdmin)
        {
            bool _setAdmin = setAdmin;
            if (setAdmin)
            {
                await AdminUser();
            }
            RedirectToPage("/Index");
        }
        

        public async Task<IActionResult> AdminUser()
        {

           

            if (ModelState.IsValid)
            {
                IdentityRole adminRole;

                bool roleExists = await _roleManager.RoleExistsAsync("Administrators");
                if (!roleExists)
                {
                    adminRole = CreateRole();

                    adminRole.Name = "Administrators";
                    adminRole.NormalizedName = "Administrators";
                    adminRole.ConcurrencyStamp = "x";

                    await _roleManager.CreateAsync(adminRole);
                }
                else
                {
                    var _adminRole = await _roleManager.FindByNameAsync("Administrators");
                    adminRole = _adminRole;
                }

                if (User != null && User.Identity != null && User.Identity.IsAuthenticated)
                {
                    var userName = User.Identity.Name;

                    IdentityUser user = await _userManager.FindByNameAsync(userName);
                    await _userManager.AddToRoleAsync(user, adminRole.Name);

                }

                TempData["success"] = "User added to admin role";
            }
            return Page();

        }

        private IdentityRole CreateRole()
        {
            try
            {
                return Activator.CreateInstance<IdentityRole>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(IdentityRole)}'. " +
                    $"Ensure that '{nameof(IdentityRole)}' is not an abstract class and has a parameterless constructor");
            }
        }

    }

}


But I'm encountering this error on this (await _userManager.AddToRoleAsync(user, adminRole.Name);) line.

System.ObjectDisposedException
  HResult=0x80131622
  Message=Cannot access a disposed object.
Object name: 'UserManager`1'.
  Source=Microsoft.Extensions.Identity.Core
  StackTrace:
   at Microsoft.AspNetCore.Identity.UserManager`1.ThrowIfDisposed()
   at Microsoft.AspNetCore.Identity.UserManager`1.<AddToRoleAsync>d__105.MoveNext()
   at Scratch.Pages.AdminUserModel.<AdminUser>d__7.MoveNext() in C:\Users\Alex\Desktop\PMP\Scratch\Scratch\Pages\AdminUser.cshtml.cs:line 90
   at Scratch.Pages.AdminUserModel.<OnGet>d__6.MoveNext() in C:\Users\Alex\Desktop\PMP\Scratch\Scratch\Pages\AdminUser.cshtml.cs:line 53
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_1(Object state)
   at System.Threading.QueueUserWorkItemCallback.<>c.<.cctor>b__6_0(QueueUserWorkItemCallback quwi)
   at System.Threading.ExecutionContext.RunForThreadPoolUnsafe[TState](ExecutionContext executionContext, Action`1 callback, TState& state)
   at System.Threading.QueueUserWorkItemCallback.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
   at System.Threading.Thread.StartCallback()

that is if I can even get past the error at the bool roleExists = await _roleManager.RoleExistsAsync("Administrators"); line which shows:

System.Threading.Tasks.TaskCanceledException
  HResult=0x8013153B
  Message=A task was canceled.
  Source=Microsoft.EntityFrameworkCore.Relational
  StackTrace:
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.<OpenInternalAsync>d__70.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.<OpenInternalAsync>d__70.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.<OpenAsync>d__66.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__19.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<InitializeReaderAsync>d__19.MoveNext()
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.<ExecuteAsync>d__7`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<MoveNextAsync>d__18.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__15`1.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__15`1.MoveNext()
   at Microsoft.AspNetCore.Identity.RoleManager`1.<RoleExistsAsync>d__33.MoveNext()
   at Scratch.Pages.AdminUserModel.<AdminUser>d__7.MoveNext() in C:\Users\Alex\Desktop\PMP\Scratch\Scratch\Pages\AdminUser.cshtml.cs:line 68
   at Scratch.Pages.AdminUserModel.<OnGet>d__6.MoveNext() in C:\Users\Alex\Desktop\PMP\Scratch\Scratch\Pages\AdminUser.cshtml.cs:line 53
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_1(Object state)
   at System.Threading.QueueUserWorkItemCallback.<>c.<.cctor>b__6_0(QueueUserWorkItemCallback quwi)
   at System.Threading.ExecutionContext.RunForThreadPoolUnsafe[TState](ExecutionContext executionContext, Action`1 callback, TState& state)
   at System.Threading.QueueUserWorkItemCallback.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
   at System.Threading.Thread.StartCallback()

70% of the time.

So either I'm missing something or I'm going about this incorrectly somehow. pls help.


Solution

  • I just needed to use public async Task OnGetAsync() and not public async void OnGet(). I didn't realize that Task operates like a function modifier (even though it is a class, which is somewhat confusing) that makes the function asynchronous.