Search code examples
asp.net-mvcasp.net-coreasp.net-identityef-code-first

ASP.NET Core 7 Identity - Custom User, Role, and PK Type


as title suggests, I want to customize the user class, the role class, and the PK types in Identity. I believe I have managed to mostly do it as I can see all of the changes in database (User and Role tables get the custom columns I wanted, and the PK types are UUID), but when I try to open the "register" page, it gives the error;

InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager1[Microsoft.AspNetCore.Identity.IdentityUser]' while attempting to activate 'EmreTest6.Areas.Identity.Pages.Account.RegisterModel'.

Here are what I've done in the project;

I created the project with "Individual Accounts" option for authentication type.

I added Npgsql provider package, and uninstalled the SqlServer provider from the project. I have deleted the whole "Migrations" folder.

I created this custom User class;

public class ApplicationUser : IdentityUser<Guid>
{
    public string Testing { get; set; }
    public string Hmm { get; set; }
}

And this custom Role class;

public class ApplicationRole : IdentityRole<Guid>
{
    public int Testing { get; set; }
}

I extended ApplicationDbContext like so;

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
...

In Program.cs;

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseNpgsql(connectionString));

builder.Services.AddDefaultIdentity<ApplicationUser>(options =>
{
    options.SignIn.RequireConfirmedAccount = false;
    options.User.RequireUniqueEmail = false;
})
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();

in _LoginPartial.cshtml;

@using EmreTest6.Data.Entities;
@using Microsoft.AspNetCore.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

I replaced the default connection string in appsettings.json with a proper PostgreSQL connection string.

Then, in package manager window, I ran this command to add my initial migration; "Add-Migration MyInitial -o Data/Migrations". The migration and the snapshot files get generated in the destination I specified, which is perfect.

I then run "Update-Database", and check with PgAdmin4 to see if the database is created. Indeed, the database is created, with "AspNetUsers" and "AspNetRoles" tables having the funny columns I specified in my "ApplicationUser" and "ApplicationRole" classes. Also, all Identity tables seem to have their PK types as UUID, which is what I wanted.

I also fully scaffolded Identity.

Project builds, and there are no errors on the main page, but when I navigate to the "Register" page, I get the error I mentioned;

InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]' while attempting to activate 'EmreTest6.Areas.Identity.Pages.Account.RegisterModel'.

Why could it be? Why is it not able to find UserManager?


Solution

  • For anyone facing the same problem; don't create your project with "individual accounts" option. instead, create it without authentication, and then scaffold identity later. on scaffolding window, specify your custom user. this will ensure that identity will properly register your custom user class, and everything will work perfectly fine!

    If you want custom user AND custom role, AND custom PK type, you can do;

    // custom identity classes
    public class ApplicationRole : IdentityRole<Guid>
    {
        public string MyRoleProperty { get; set; }
    }
    
    public class ApplicationUser : IdentityUser<Guid>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    // DbContext 
    public class IdentityDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
    {
        ...
    }
    
    // Program.cs
    builder.Services.AddDefaultIdentity<ApplicationUser>(options => {
        options.SignIn.RequireConfirmedAccount = true;
        options.User.RequireUniqueEmail = true;
        })
        .AddRoles<ApplicationRole>()
        .AddEntityFrameworkStores<IdentityDbContext>();
    

    and you are golden! if you did the exact same thing with the project being created with "individual accounts" option, it would not have worked. This is because manual scaffolding does a lot of the configuration in the background for you when you specified your custom user class in the scaffolder window (replacing IdentityUser with ApplicationUser throughout all classes etc), lifting off a lot of weight from your shoulders.