cannot convert from 'IdentityRole'(Microsoft) to 'ApplicationRole'(Custom Extension)

Joachim Petersen 21 Reputation points
2021-09-27T11:12:02.25+00:00

Hello, I have been stuck on this for a day - and I have no idea what the issue is other than a 'bug' in Microsoft.AspNetCore.Identity.IdentityRole?

I have utilized this following implementation for custom stores with Dapper and MSSQL: https://github.com/simonfaltum/AspNetCore.Identity.Dapper/

My ApplicationRole.cs:

using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using AspNetCore.Identity.Custom.Stores;

namespace AspNetCore.Identity.Custom.Models
{
    public class ApplicationRole : Microsoft.AspNetCore.Identity.IdentityRole
    {

        internal List<Claim> Claims { get; set; }
    }
}

usage in 'identity' controller where I try adding a role through the ApplicationRole.

[HttpPost]
[Route("register-admin")]
public async Task<IActionResult> RegisterAdmin([FromBody] RegisterModel model)
{
    var userExists = await userManager.FindByNameAsync(model.Username);
    if (userExists != null)
        return StatusCode(StatusCodes.Status500InternalServerError, new CustomErrorResponse { Message = "Error", Description = "User already exists!" });

    ApplicationUser user = new ApplicationUser()
    {
        Email = model.Email,
        SecurityStamp = Guid.NewGuid().ToString(),
        UserName = model.Username
    };
    var result = await userManager.CreateAsync(user, model.Password);
    if (!result.Succeeded)
        return StatusCode(StatusCodes.Status500InternalServerError, new CustomErrorResponse { Message = "Error", Description = "User creation failed! Please check user details and try again." }); //use maybe identityError see custom identity impl

    if (!await roleManager.RoleExistsAsync(UserRoles.Admin))
    {
        await roleManager.CreateAsync(new IdentityRole("hey")); // ERROR: Argument 1: cannot convert from 'Microsoft.AspNetCore.Identity.IdentityRole' to 'AspNetCore.Identity.Custom.Models.ApplicationRole'
    }
    //await roleManager.CreateAsync(new IdentityRole(UserRoles.Admin));
    if (!await roleManager.RoleExistsAsync(UserRoles.User))
        //await roleManager.CreateAsync(new IdentityRole(UserRoles.User));

    if (await roleManager.RoleExistsAsync(UserRoles.Admin))
    {
        await userManager.AddToRoleAsync(user, UserRoles.Admin);
    }

    return Ok("User created successfully!");
}

}

I get following error: Argument 1: cannot convert from 'Microsoft.AspNetCore.Identity.IdentityRole' to 'AspNetCore.Identity.Custom.Models.ApplicationRole' on the await roleManager.CreateAsync(new IdentityRole("hey"));

I'm using Asp.Net Core 3.1, identity implimentation added by following in startup.cs:

services.AddIdentityCore<ApplicationUser>(options =>
            {
                options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
                options.User.RequireUniqueEmail = true;

                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 8;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = true;
            })
            .AddRoles<ApplicationRole>()
            .AddDapperStores(options =>
            {
                options.ConnectionString = Configuration.GetConnectionString("defaultConnection");
                options.DbSchema = "identity";
            });

            // Adding Authentication  
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })

I have added both Microsoft.Extensions.Identity.Stores on the custom identity store (AspNetCore.Identity.Dapper) and my main project where I house the Controller.

No other errors is present in the solution; and no runtime errors when creating a user with the function when 'await roleManager.CreateAsync(new IdentityRole(UserRoles.User));' is removed.

I am unable to understand the issue as I have it 'clearly' derived from Microsoft.AspNetCore.Identity.IdentityRole.

Has somebody experienced this issue before? or am I just mistaken in my usage of the implementations?

Microsoft Identity Manager
Microsoft Identity Manager
A family of Microsoft products that manage a user's digital identity using identity synchronization, certificate management, and user provisioning.
617 questions
ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,190 questions
{count} votes

Accepted answer
  1. Zhi Lv - MSFT 32,016 Reputation points Microsoft Vendor
    2021-09-29T03:04:30.597+00:00

    Hi @Joachim Petersen ,

    By default, the Identity User and Identity Role's id property are GUID type, there is no need to override it to GUID type again in the ApplicationRole's constructor method. More detail information, see Identity model customization in ASP.NET Core.

    await roleManager.CreateAsync(new IdentityRole("hey")); // ERROR: Argument 1: cannot convert from 'Microsoft.AspNetCore.Identity.IdentityRole' to 'AspNetCore.Identity.Custom.Models.ApplicationRole'

    About this error, since you have already configured the Identity use custom ApplicationRole model, when register the RoleManager and create new Role, you should use the ApplicationRole model, instead of IdentityRole.

    You can refer the following sample to add custom role data to Identity in an ASP.NET Core project.

    ApplicationRole.cs:

    public class ApplicationRole:IdentityRole  
    {  
        public DateTime CreateDate { get; set; }  
    }  
    

    ApplicationDbContext.cs

    public class ApplicationDbContext : IdentityDbContext  
    {  
        public DbSet<ApplicationUser> ApplicationUsers { get; set; }  
        public DbSet<ApplicationRole> ApplicationRoles { get; set; }  
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)  
            : base(options)  
        {  
        }  
        protected override void OnModelCreating(ModelBuilder builder)  
        {  
            base.OnModelCreating(builder);   
        }  
    }  
    

    Configure the Identity in the ConfigureServices method:

        services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.SignIn.RequireConfirmedAccount = true)  
                 .AddEntityFrameworkStores<ApplicationDbContext>()  
                 .AddDefaultTokenProviders()  
                 .AddDefaultUI();  
    

    Add RoleController with the following code: Here we should use the ApplicationRole model, instead of IdentityRole model.

    public class RoleController : Controller  
    {  
        private readonly RoleManager<ApplicationRole> _roleManager;   
        private readonly ApplicationDbContext dbcontext;   
        public RoleController(RoleManager<ApplicationRole> roleManager,  ApplicationDbContext db)  
        {  
            _roleManager = roleManager;   
            dbcontext = db;  
        }   
    
        public IActionResult Index()  
        {   
            var roles = _roleManager.Roles.ToList();  
            return View(roles);  
        }   
        public IActionResult Create()  
        {  
            return View(new ApplicationRole());  
        }  
    
        [HttpPost]  
        public async Task<IActionResult> Create(ApplicationRole role)  
        {   
            role.CreateDate = DateTime.Now;  
            await _roleManager.CreateAsync(role);  
            return RedirectToAction("Index");  
        }  
    }  
    

    The result as below:

    136027-1.gif


    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,
    Dillion


2 additional answers

Sort by: Most helpful
  1. Joachim Petersen 21 Reputation points
    2021-09-27T15:56:27.04+00:00

    So the correct approach is following?

    using System.Collections.Generic;
    using System.Security.Claims;
    using Microsoft.AspNetCore.Identity;
    using AspNetCore.Identity.Custom.Stores;
    using System;
    
    namespace AspNetCore.Identity.Custom.Models
    {
        public class ApplicationRole : IdentityRole<string>
        {
            /// <summary>
            /// Initializes a new instance of <see cref="IdentityRole"/>.
            /// </summary>
            /// <remarks>
            /// The Id property is initialized to form a new GUID string value.
            /// </remarks>
            public ApplicationRole()
            {
                Id = Guid.NewGuid().ToString();
            }
    
            /// <summary>
            /// Initializes a new instance of <see cref="IdentityRole"/>.
            /// </summary>
            /// <param name="roleName">The role name.</param>
            /// <remarks>
            /// The Id property is initialized to form a new GUID string value.
            /// </remarks>
            public ApplicationRole(string roleName) : this()
            {
                Name = roleName;
            }
    
            internal List<Claim> Claims { get; set; }
        }
    }
    

    I though that the functions not implimented would be inherited from the IdentityRole by it being a derived class; what are the benefits of the derived class if methods are not inherited?

    0 comments No comments

  2. Zhi Lv - MSFT 32,016 Reputation points Microsoft Vendor
    2021-10-01T07:44:57.363+00:00

    Hi @Joachim Petersen ,

    Then I receive following error "Error CS1729 'ApplicationRole' does not contain a constructor that takes 1 arguments" when I try and execute: "await roleManager.CreateAsync(new ApplicationRole(UserRoles.Admin));" hence I am using my custom application role, but still receive errors like the methods are not inherited from the IdentityRole.

    If you want to create the new role using the constructor method, as in your 2nd comment, you have to add the related constructor method with the related arguments.

    As seen in my 2nd comment; it worked when I reimplimented the ApplicationRole(string roleName) methods; but according to your answer, I still understand that it should be inhertied by the IdentityRole?, which my applicationrole is derived from.

    Yes, to add the custom data to the Identity Role table, the custom class should inherit the IdentityRole. You can refer the following articles:

    Add, download, and delete custom user data to Identity in an ASP.NET Core project

    Identity model customization in ASP.NET Core