UserManager используется с различными хранилищами (UserStore, UserEmailStore, UserClaimStore, UserLockoutStore и т. Д.)

Я пытаюсь реализовать UserStore, но я хотел бы также реализовать UserEmailStore и UserLockoutStore и другие. Как я заметил, все User*Store основаны на UserStore, никаких проблем. Но я заглянул в UserManager и обнаружил для меня странную вещь. Вы можете добавить несколько типов хранилищ в UserManager, но всегда только один. Но UserManager может работать со всеми из них в зависимости от того, какой тип вы ввели.

Пример Fox-метода GetLockoutEndDateAsync из 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);
}

Метод this.GetUserLockoutStore выглядит следующим образом

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

Есть и другие методы, такие как

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

Таким образом, это означает, что хранилище должно основываться на правильном интерфейсе, который вы хотите использовать.

У меня вопрос, как с этим бороться? Должен ли я реализовать одно хранилище на основе всех возможных интерфейсов User*Store? Или вы можете предложить другое решение?

заранее спасибо

1 ответ

Да, реализация необходимых интерфейсов в виде "функций" в одном хранилище - это простой способ сделать это, а также способ реализации поставщика ASP.NET Core Identity EF Core (см. Здесь).

/// <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>
{
}

Вам нужно только реализовать интерфейсы, которые вы собираетесь поддерживать, и не включать остальные.

Если по какой-либо причине (принцип единой ответственности) это невозможно (т. Е. Потому, что вам нужно использовать совершенно другой тип базы данных или какой-либо веб-сервис или Active Directory для него), то вы можете реализовать отдельные хранилища и использовать шаблон фасада для обтекания. это и вводить ваши отдельные магазины в фасад и вводить фасад.

Но это больше работы и требует больше настройки DI, чтобы сделать это. Но выполнимо.

Другие вопросы по тегам