Подтвердить что ты не робот

Как зарегистрировать пользовательский UserStore & UserManager в DI

Вот моя настройка:

public class ApplicationUser : IdentityUser<Guid>
{
}
public class ApplicationRole : IdentityRole<Guid>
{
}
public class ApplicationUserLogin : IdentityUserLogin<Guid>
{
}
public class ApplicationUserClaim : IdentityUserClaim<Guid>
{
}
public class ApplicationRoleClaim : IdentityRoleClaim<Guid>
{
}

Вот определение моего UserStore

public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, MyContext, Guid>
{
    public ApplicationUserStore(MyContext context, IdentityErrorDescriber describer = null)
        : base(context, describer)
    {
    }
}

Вот определение моего UserManager

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
        IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, IEnumerable<IUserTokenProvider<ApplicationUser>> tokenProviders,
        ILoggerFactory logger, IHttpContextAccessor contextAccessor)
        : base(
            store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
            tokenProviders, logger, contextAccessor)
    {
    }
}

Вот определение моего DbContext:

public class MyContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}

И вот мой Startup.cs

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.Get("Data:DbConnection")));

        services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<MyContext, Guid>()
            .AddUserStore<ApplicationUserStore>()
            .AddRoleStore<ApplicationRoleStore>()
            .AddUserManager<ApplicationUserManager>()
            .AddRoleManager<ApplicationRoleManager>()
            .AddDefaultTokenProviders();

        var builder = new ContainerBuilder();
        builder.Populate(services);
        var container = builder.Build();
        return container.Resolve<IServiceProvider>();
    }

Зависимость этого конструктора будет работать:

public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)

Этого не будет:

public AccountController(ApplicationUserManager userManager, SignInManager<ApplicationUser> signInManager)

У кого-то есть идея о том, что я делаю неправильно?

4b9b3361

Ответ 1

DI в целом предназначен для разработки с интерфейсом; .AddUserManager<ApplicationUserManager>() указывает реализацию UserManager<>, а не интерфейс службы. Это означает, что он все еще ожидает, что вы получите UserManager<ApplicationUser> и будете использовать его именно так; он даст вам ApplicationUserManager.

Я предполагаю, что у вас есть дополнительные методы, которые вы хотите использовать на своем ApplicationUserManager. Если нет, просто используйте конструктор зависимости так, как он работает, и наслаждайтесь развитием интерфейса. Если это так, у вас есть 3 варианта:

  • Использовать расширение с помощью композиции, а не наследования. Вместо того, чтобы наследовать от UserManager<>, напишите ApplicationUserManager в качестве класса оболочки; вы можете включить его в конструктор. Это должно предоставить вам все функциональные возможности внутри ApplicationUserManager.

  • Добавьте его как есть в каркас DI самостоятельно. Это не так сложно, как кажется, поскольку UserManager<> не имеет реального состояния:

    services.AddScoped<ApplicationUserManager>();
    

    Недостаток заключается в том, что у вас фактически есть два объекта UserManager<> для области пользователя; в результате может быть некоторая неэффективность. Из состояния текущего кода я не думаю, что это так.

  • Запишите его как методы расширения. Если у вас есть несколько зависимостей, а не только базовая функциональность UserManager<>, это может быть очень сложно.

Ответ 2

Теперь я на ASP.NET Core 1.1, и это поведение было исправлено.

Я могу легко реализовать собственный UserManager и UserStore, а затем загрузите приложение следующим образом:

// identity models
services
    .AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
    .AddUserManager<ApplicationUserManager>()
    .AddUserStore<ApplicationUserStore>()
    .AddDefaultTokenProviders();

и вводить как UserManager, так и UserStore в мой контроллер без каких-либо проблем:

public AccountController(
    IIdentityServerInteractionService interaction,
    IClientStore clientStore,
    IHttpContextAccessor httpContextAccessor,
    ApplicationUserManager userManager,
    SignInManager<ApplicationUser> signInManager,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory)
{
    _interaction = interaction;
    _userManager = userManager;
    _signInManager = signInManager;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
    _account = new AccountService(_interaction, httpContextAccessor, clientStore);
}