添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Recently I've answered a question on StackOverflow website and the question was how to create an overloaded method for both AddToRoleAsync() and IsInRoleAync() to look for the role by its ID and not by name. I this post I show how to extend UserManager class in the way it is implemented and access some internals of UserManager .

First way to extend the UserManager is using extension methods. In some cases you don't need to create another class to extend UserManager and by implementing some extensions methods to achieve what you want.

public static class UserManagerExtensions
    public static Task<ApplicationUser> FindByNameAsync(this UserManager<ApplicationUser> userManager, string name)
        return userManager?.Users?.FirstOrDefaultAsync(um => um.UserName == name);
    public static Task<ApplicationUser> FindByCardIDAsync(this UserManager<ApplicationUser> userManager, string cardId)
        return userManager?.Users?.FirstOrDefaultAsync(um => um.CardId == cardId);
    Enter fullscreen mode
    Exit fullscreen mode
public class ApplicationUserManager : UserManager<IdentityUser>
    public ApplicationUserManager(
        IUserStore<IdentityUser> store,
        IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<IdentityUser> passwordHasher,
        IEnumerable<IUserValidator<IdentityUser>> userValidators,
        IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,
        ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors,
        IServiceProvider services,
        ILogger<UserManager<IdentityUser>> logger)
        : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    Enter fullscreen mode
    Exit 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 is store and for implementing another method to add role to a user by identifier we need that.

public class ApplicationUserManager : UserManager<IdentityUser>
    private readonly UserStore<IdentityUser, IdentityRole, ApplicationDbContext, string, IdentityUserClaim<string>,
        IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>> 
        _store;
    public ApplicationUserManager(
        IUserStore<IdentityUser> store,
        _store = (UserStore<IdentityUser, IdentityRole, ApplicationDbContext, string, IdentityUserClaim<string>,
            IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>)store;
    Enter fullscreen mode
    Exit fullscreen mode

Before explaining why I cast the interface of _store to generic UserStore class with 9 parameters, let's look at the implementation AddToRoleAsync method (in such a case as good practice always check the source code of identity to get an idea to implement what you want)
Inside AddToRoleAsync implementation FindRoleAsync method is called and I checked FindRoleAsync implementation:
Inside FindRoleAsync Role property is used to fetch a role by name and again I check Role definition:
I noticed I cannot use the Role property to the get role by identifier because it's private property but I noticed there is a public property of DbContext that I can use. So that's why I cast IUserStor interface to UserStore class to have access to Context property.

It's time to implement AddToRoleAsync() and IsInRoleAync():

public virtual async Task<IdentityResult> AddToRoleByRoleIdAsync(IdentityUser user, string roleId)
    ThrowIfDisposed();
    if (user == null)
        throw new ArgumentNullException(nameof(user));
    if (string.IsNullOrWhiteSpace(roleId))
        throw new ArgumentNullException(nameof(roleId));
    if (await IsInRoleByIdAsync(user, roleId, CancellationToken))
        return IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(roleId));
    _store.Context.Set<IdentityUserRole<string>>().Add(new IdentityUserRole<string> { RoleId = roleId, UserId = user.Id });
    return await UpdateUserAsync(user);
public async Task<bool> IsInRoleByIdAsync(IdentityUser user, string roleId, CancellationToken cancellationToken = default(CancellationToken))
    cancellationToken.ThrowIfCancellationRequested();
    ThrowIfDisposed();
    if (user == null)
        throw new ArgumentNullException(nameof(user));
    if (string.IsNullOrWhiteSpace(roleId))
        throw new ArgumentNullException(nameof(roleId));
    var role = await _store.Context.Set<IdentityRole>().FindAsync(roleId);
    if (role == null)
        return false;
    var userRole = await _store.Context.Set<IdentityUserRole<string>>().FindAsync(new object[] { user.Id, roleId }, cancellationToken);
    return userRole != null;
    Enter fullscreen mode
    Exit fullscreen mode
services.AddIdentity<IdentityUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false)
   .AddEntityFrameworkStores<ApplicationDbContext>()
   .AddUserManager<ApplicationUserManager>() // Add ApplicationUserManager
   .AddDefaultTokenProviders()
   .AddDefaultUI();
    Enter fullscreen mode
    Exit fullscreen mode
          

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/...

Built on Forem — the open source software that powers DEV and other inclusive communities.

Made with love and Ruby on Rails. DEV Community © 2016 - 2024.