question

JoachimPetersen-5071 avatar image
0 Votes"
JoachimPetersen-5071 asked ZhiLv-MSFT commented

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

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?

dotnet-aspnet-core-webapimicrosoft-identity-managerdotnet-aspnet-core-security
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

C# is a strongly typed language. Therefore it is not possible to assign the custom model AspNetCore.Identity.Custom.Models.ApplicationRole to Microsoft.AspNetCore.Identity.IdentityRole. You must create a new Microsoft.AspNetCore.Identity.IdentityRole type by copying the property values form AspNetCore.Identity.Custom.Models.ApplicationRole. This is called a projection in EF. Also, there are libraries, like AutoMapper, that are designed to do this type of conversion between the data access layer and UI models.


0 Votes 0 ·
ZhiLv-MSFT avatar image
0 Votes"
ZhiLv-MSFT answered ZhiLv-MSFT converted comment to answer

Hi @JoachimPetersen-5071,

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


1.gif (754.2 KiB)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I do get and understand the example you have attached; I am not using Entity Framework - but if I simply have my ApplicationRole consist of:

 public class ApplicationRole : IdentityRole<string>
         {
             //public ApplicationRole()
             //{
             //    Id = Guid.NewGuid().ToString();
             //}
      
             //public ApplicationRole(string roleName) : this()
             //{
             //    Name = roleName;
             //}
        
             //All code above is commented/disabled
    
             internal List<Claim> Claims { get; set; }
         }

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.

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.

0 Votes 0 ·
JoachimPetersen-5071 avatar image
0 Votes"
JoachimPetersen-5071 answered

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?

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

ZhiLv-MSFT avatar image
0 Votes"
ZhiLv-MSFT answered ZhiLv-MSFT commented

Hi @JoachimPetersen-5071,

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

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Above comment from ZhiLv-MSFT links to resources that gives the answer to my problem; sadly I cannot mark above comment as an answer.

0 Votes 0 ·
ZhiLv-MSFT avatar image ZhiLv-MSFT JoachimPetersen-5071 ·

Hi @JoachimPetersen-5071,

Glad to hear it did help resolve the problem. I have converted the comment as an answer, hope it can help other community members who meet the similar issue find the answer quickly. Have a wonderful day!

0 Votes 0 ·