简体   繁体   中英

UserManager used with different stores (UserStore, UserEmailStore, UserClaimStore, UserLockoutStore etc.)

I am trying to implement UserStore but I would like also to implement UserEmailStore and UserLockoutStore and the others. As I noticed all User*Store are based on UserStore, no problem. But I took a look into UserManager and I found, for me, strange thing. You can inject several types of store to UserManager but always only one. But UserManager can works with all of them based on what type you injected.

Fox example method GetLockoutEndDateAsync from UserManager

public virtual async Task<DateTimeOffset?> GetLockoutEndDateAsync(TUser user)
{
  this.ThrowIfDisposed();
  IUserLockoutStore<TUser> userLockoutStore = this.GetUserLockoutStore();
  if ((object) user == null)
    throw new ArgumentNullException("user");
  TUser user1 = user;
  CancellationToken cancellationToken = this.CancellationToken;
  return await userLockoutStore.GetLockoutEndDateAsync(user1, cancellationToken);
}

Method this.GetUserLockoutStore looks like this

internal IUserLockoutStore<TUser> GetUserLockoutStore()
{
  IUserLockoutStore<TUser> userLockoutStore = this.Store as IUserLockoutStore<TUser>;
  if (userLockoutStore != null)
    return userLockoutStore;
  throw new NotSupportedException(Resources.StoreNotIUserLockoutStore);
}

There are other methods like

  • GetEmailStore
  • GetPhoneNumberStore
  • GetClaimStore
  • GetLoginStore
  • ...

So it means store must be based on correct interface you want to use.

My question is, how to deal with this? Should I implement one store based on all possible User*Store interfaces? Or can you suggest another solution?

Thanks in advance

Yes, implementing the required interfaces as "features" in one single store is the straightforward way to do it and it's also how ASP.NET Core Identity EF Core provider is implemented (see here )

/// <summary>
/// Represents a new instance of a persistence store for the specified user and role types.
/// </summary>
/// <typeparam name="TUser">The type representing a user.</typeparam>
/// <typeparam name="TRole">The type representing a role.</typeparam>
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
/// <typeparam name="TUserClaim">The type representing a claim.</typeparam>
/// <typeparam name="TUserRole">The type representing a user role.</typeparam>
/// <typeparam name="TUserLogin">The type representing a user external login.</typeparam>
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
/// <typeparam name="TRoleClaim">The type representing a role claim.</typeparam>
public abstract class UserStore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
    IUserLoginStore<TUser>,
    IUserRoleStore<TUser>,
    IUserClaimStore<TUser>,
    IUserPasswordStore<TUser>,
    IUserSecurityStampStore<TUser>,
    IUserEmailStore<TUser>,
    IUserLockoutStore<TUser>,
    IUserPhoneNumberStore<TUser>,
    IQueryableUserStore<TUser>,
    IUserTwoFactorStore<TUser>,
    IUserAuthenticationTokenStore<TUser>
    where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin>
    where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
    where TContext : DbContext
    where TKey : IEquatable<TKey>
    where TUserClaim : IdentityUserClaim<TKey>
    where TUserRole : IdentityUserRole<TKey>
    where TUserLogin : IdentityUserLogin<TKey>
    where TUserToken : IdentityUserToken<TKey>
    where TRoleClaim : IdentityRoleClaim<TKey>
{
}

You only need to implement the interfaces you are going to support and leave the other ones out.

If for some reason (Single Responsibility Principle) this is not possible (ie because you need to use a completely different type of database or some webservice or Active Directory for it), then you can implement individual stores and use a facade pattern to wrap around it and inject your individual stores into the facade and inject the facade.

But it's more work and requires more setup of DI to do it. But doable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM