Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Mohsen Esmailpour
Mohsen Esmailpour

Posted on

     

How to extend identity UserManager class

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);}....}
Enter fullscreen modeExit fullscreen mode

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){}}
Enter fullscreen modeExit fullscreen mode

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;}}
Enter fullscreen modeExit fullscreen mode

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)
Alt Text
InsideAddToRoleAsync implementationFindRoleAsync method is called and I checkedFindRoleAsync implementation:
Alt Text
InsideFindRoleAsyncRole property is used to fetch a role by name and again I checkRole definition:
Alt Text
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;}
Enter fullscreen modeExit fullscreen mode

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();
Enter fullscreen modeExit fullscreen mode

The last thing I want to mention is that if you have extendedIdentityUser,IdentityRole, ... classes, replace them with your own implementation.

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
uthmanrahimi profile image
Uthman Rahimi
Software Engineer who does not use Resharper.
  • Location
    Tehran
  • Education
    MA of Computer Engineering
  • Joined
• Edited on• Edited

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?

CollapseExpand
 
moesmp profile image
Mohsen Esmailpour
I'm a software engineer with several years of experience in developing software mainly working on web platforms with the Microsoft .NET stack.
  • Location
    Amsterdam
  • Education
    Bachelor of Arts (BA), Computer Software Engineering
  • Work
    Software 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/...

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I'm a software engineer with several years of experience in developing software mainly working on web platforms with the Microsoft .NET stack.
  • Location
    Amsterdam
  • Education
    Bachelor of Arts (BA), Computer Software Engineering
  • Work
    Software Engineer
  • Joined

More fromMohsen Esmailpour

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp