I’ve recently been working on an application with basic ASP.NET Core identity. We’ve generated the necessary SQL tables via EntityFramework migrations. AspNetUsers
, AspNetRoles
, AspNetUserRoles
, the gang’s all here! We have a defined ApplicationUser
entity, inheriting from IdentityUser
, and since the Roles
tables are in place, we should be good to go! Fire up RoleManager
, create a role, assign a user to it!
But wait, something’s wrong! When we try to use RoleManager
on our Register page, our application throws an exception:
InvalidOperationException: Cannot provide a value for property ‘RoleManager’ on type ‘MyApp.Pages.Register’. There is no registered service of type ‘Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]‘.
What gives?
Setting Up Identity with AddIdentityCore<>
In our application setup, we’re calling AddIdentityCore<ApplicationUser>
on our IServiceCollection services
:
services.AddIdentityCore<ApplicationUser>()
.AddEntityFrameworkStores<MyDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
Let’s have a closer look at the summary documentation comment for AddIdentityCore<>
:
Adds and configures the identity system for the specified User type. Role services are not added by default but can be added with AddRoles
().
If you’ve used identity in the past, Roles were always included automatically. Now, however, they’re not enabled by default!
First Attempt at Following Instructions
Okay, it gives us instructions on what to do. Call AddRoles<TIdentity>()
and we’re set! We’ll use the default IdentityRole
class as we have no reason to override or extend it in our application.
services.AddIdentityCore<ApplicationUser>()
.AddEntityFrameworkStores<MyDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders()
.AddRoles<IdentityRole>();
All set? Not exactly. After adding this line, we get a different error. The application won’t even start up! Different errors are progress, but we still would rather our application just work. What do we get this time?
System.InvalidOperationException: Unable to resolve service for type ‘Microsoft.AspNetCore.Identity.IRoleStore`1[Microsoft.AspNetCore.Identity.IdentityRole]’ while attempting to activate ‘Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]‘.
Something about IRoleStore
not being registered? Do we have to add it ourselves? You may find that after AddIdentityCore<>
you can indeed call AddRoleStore<>
. Following the error’s description, we could try that:
services.AddIdentityCore<ApplicationUser>()
.AddEntityFrameworkStores<MyDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders()
.AddRoles<IdentityRole>()
.AddRoleStore<IdentityRole>();
But this feels wrong… AddIdentityCore<>
’s instruction was just to call AddRoles<>
! And in fact, calling AddRoleStore<>
doesn’t solve our issue:
System.ArgumentException: Implementation type ‘Microsoft.AspNetCore.Identity.IdentityRole’ can’t be converted to service type ‘Microsoft.AspNetCore.Identity.IRoleStore`1[Microsoft.AspNetCore.Identity.IdentityRole]’
Quite the rabbit hole we’re falling down. I fell for a while, trying various permutations of the above role-adding methods (along with a few others), before stumbling upon the solution.
The Secret Sauce
The order matters! Calling AddRoles<>
is indeed all you have to do, but you must call it before AddEntityFrameWorkStores<>
!
services.AddIdentityCore<ApplicationUser>()
.AddRoles<IdentityRole>() // Must be called BEFORE AddEntityFrameworkStores<>
.AddEntityFrameworkStores<MyDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
Once we do that, we can use RoleManager
and all role-based things like normal.
This gotcha got me good and I wanted to share this tidbit with the world, lest you run into the same errors or problems.