Recently I've answered a question onStackOverflow website and the question was how to create an overloaded method for bothAddToRoleAsync()
andIsInRoleAync()
to look for the role by its ID and not by name. I this post I show how to extendUserManager
class in the way it is implemented and access some internals ofUserManager
.
First way to extend theUserManager
is using extension methods. In some cases you don't need to create another class to extendUserManager
and by implementing some extensions methods to achieve what you want.
publicstaticclassUserManagerExtensions{publicstaticTask<ApplicationUser>FindByNameAsync(thisUserManager<ApplicationUser>userManager,stringname){returnuserManager?.Users?.FirstOrDefaultAsync(um=>um.UserName==name);}publicstaticTask<ApplicationUser>FindByCardIDAsync(thisUserManager<ApplicationUser>userManager,stringcardId){returnuserManager?.Users?.FirstOrDefaultAsync(um=>um.CardId==cardId);}....}
In some cases like create an overloaded method forAddToRoleAsync()
to add a role to a user by role ID and not by role name, the extension method doesn't work and it requires a custom implementation ofUserManager
class. Lets spin up a new class that inherits from the built-inUserManager
:
publicclassApplicationUserManager:UserManager<IdentityUser>{publicApplicationUserManager(IUserStore<IdentityUser>store,IOptions<IdentityOptions>optionsAccessor,IPasswordHasher<IdentityUser>passwordHasher,IEnumerable<IUserValidator<IdentityUser>>userValidators,IEnumerable<IPasswordValidator<IdentityUser>>passwordValidators,ILookupNormalizerkeyNormalizer,IdentityErrorDescribererrors,IServiceProviderservices,ILogger<UserManager<IdentityUser>>logger):base(store,optionsAccessor,passwordHasher,userValidators,passwordValidators,keyNormalizer,errors,services,logger){}}
You can generate the constructor automatically with Visual Studio by using the Generate Constructor Quick Action (by pressing CTRL-ENTER).
The first parameter of constructor isstore
and for implementing another method to add role to a user by identifier we need that.
publicclassApplicationUserManager:UserManager<IdentityUser>{privatereadonlyUserStore<IdentityUser,IdentityRole,ApplicationDbContext,string,IdentityUserClaim<string>,IdentityUserRole<string>,IdentityUserLogin<string>,IdentityUserToken<string>,IdentityRoleClaim<string>>_store;publicApplicationUserManager(IUserStore<IdentityUser>store,...){_store=(UserStore<IdentityUser,IdentityRole,ApplicationDbContext,string,IdentityUserClaim<string>,IdentityUserRole<string>,IdentityUserLogin<string>,IdentityUserToken<string>,IdentityRoleClaim<string>>)store;}}
Before explaining why I cast the interface of_store
to genericUserStore
class with 9 parameters, let's look at the implementationAddToRoleAsync method (in such a case as good practice always check the source code of identity to get an idea to implement what you want)
InsideAddToRoleAsync
implementationFindRoleAsync
method is called and I checkedFindRoleAsync
implementation:
InsideFindRoleAsync
Role
property is used to fetch a role by name and again I checkRole
definition:
I noticed I cannot use theRole
property to the get role by identifier because it's private property but I noticed there is a public property ofDbContext
that I can use. So that's why I castIUserStor
interface toUserStore
class to have access toContext
property.
It's time to implementAddToRoleAsync()
andIsInRoleAync()
:
publicvirtualasyncTask<IdentityResult>AddToRoleByRoleIdAsync(IdentityUseruser,stringroleId){ThrowIfDisposed();if(user==null)thrownewArgumentNullException(nameof(user));if(string.IsNullOrWhiteSpace(roleId))thrownewArgumentNullException(nameof(roleId));if(awaitIsInRoleByIdAsync(user,roleId,CancellationToken))returnIdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(roleId));_store.Context.Set<IdentityUserRole<string>>().Add(newIdentityUserRole<string>{RoleId=roleId,UserId=user.Id});returnawaitUpdateUserAsync(user);}publicasyncTask<bool>IsInRoleByIdAsync(IdentityUseruser,stringroleId,CancellationTokencancellationToken=default(CancellationToken)){cancellationToken.ThrowIfCancellationRequested();ThrowIfDisposed();if(user==null)thrownewArgumentNullException(nameof(user));if(string.IsNullOrWhiteSpace(roleId))thrownewArgumentNullException(nameof(roleId));varrole=await_store.Context.Set<IdentityRole>().FindAsync(roleId);if(role==null)returnfalse;varuserRole=await_store.Context.Set<IdentityUserRole<string>>().FindAsync(newobject[]{user.Id,roleId},cancellationToken);returnuserRole!=null;}
I used_store.Context.Set<IdentityRole>()
to find by a role by identifier.
Next step is to registerApplicationUserManager
:
services.AddIdentity<IdentityUser,IdentityRole>(options=>options.SignIn.RequireConfirmedAccount=false).AddEntityFrameworkStores<ApplicationDbContext>().AddUserManager<ApplicationUserManager>()// Add ApplicationUserManager.AddDefaultTokenProviders().AddDefaultUI();
The last thing I want to mention is that if you have extendedIdentityUser
,IdentityRole
, ... classes, replace them with your own implementation.
Top comments(2)

- LocationTehran
- EducationMA of Computer Engineering
- Joined
Thank you Mohsen.
I've got a question although it is not related to this topic, but I'd liked to know about it, in bothIsInRoleByIdAsync()
,AddToRoleByRoleIdAsync()
methods, there is a method namedThrowIfDisposed()
, what are its purposes?

- LocationAmsterdam
- EducationBachelor of Arts (BA), Computer Software Engineering
- WorkSoftware Engineer
- Joined
It’s part of default implementation of UserStore class and it checks whether the current instance is disposed or not.
github.com/dotnet/aspnetcore/blob/...
For further actions, you may consider blocking this person and/orreporting abuse