简体   繁体   English

如何在 DI 中注册自定义 UserStore & UserManager

[英]How to register custom UserStore & UserManager in DI

Here is my setup:这是我的设置:

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

Here is the definition of my UserStore这是我的 UserStore 的定义

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

Here is the definition of my UserManager这是我的 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)
    {
    }
}

Here is the definition of my DbContext:这是我的 DbContext 的定义:

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

And here is my Startup.cs这是我的 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>();
    }

The dependency of this constructor will work:此构造函数的依赖项将起作用:

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

This one won't:这个不会:

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

Anyone has an idea on what I'm doing wrong?任何人都知道我做错了什么?

DI in general is intended for interface-driven development; DI 通常用于接口驱动的开发; .AddUserManager<ApplicationUserManager>() specifies an implementation UserManager<> , not the service interface. .AddUserManager<ApplicationUserManager>()指定一个实现UserManager<> ,而不是服务接口。 That means that it's still expecting you to get UserManager<ApplicationUser> and only use it that way;这意味着它仍然希望您获得UserManager<ApplicationUser>并且仅以这种方式使用它; it'll give you an ApplicationUserManager .它会给你一个ApplicationUserManager

I'm assuming that you have additional methods you want to use on your ApplicationUserManager .我假设您有要在ApplicationUserManager上使用的其他方法。 If not, just use the dependency constructor the way it works and enjoy the interface-driven development.如果没有,只需按照它的工作方式使用依赖构造函数并享受接口驱动的开发。 If so, you have 3 options:如果是这样,您有 3 个选择:

  1. Use extension via composition rather than inheritance.通过组合而不是继承来使用扩展。 Rather than inheriting from UserManager<> , write ApplicationUserManager as a wrapper class;与其从UserManager<>继承,不如将ApplicationUserManager写成一个包装类; you can include it in the constructor.您可以将其包含在构造函数中。 This should give you all the functionality you need inside of the ApplicationUserManager .这应该为您提供ApplicationUserManager所需的所有功能。

  2. Add it as-is to the DI framework yourself.将其按原样添加到 DI 框架中。 This isn't as difficult as it sounds, since the UserManager<> has no real state itself:这并不像听起来那么困难,因为UserManager<>本身没有真实的状态:

     services.AddScoped<ApplicationUserManager>();

    The disadvantage here is that you'll actually have two UserManager<> objects for the user's scope;这里的缺点是您实际上将有两个UserManager<>对象用于用户范围; there could be some inefficiencies as a result.因此可能会出现一些低效率的情况。 From the state of the current code, I don't think it is.从当前代码的状态来看,我认为不是。

  3. Write it as extension methods.把它写成扩展方法。 If you have a number of dependencies and not just the UserManager<> 's base functionality, this could be really complex.如果您有许多依赖项,而不仅仅是UserManager<>的基本功能,那么这可能非常复杂。

I am now on ASP.NET Core 1.1 and this behavior has been fixed.我现在使用的是 ASP.NET Core 1.1,并且此行为已得到修复。

I can easily implement my own UserManager and UserStore, then bootstrap the app as following:我可以轻松实现自己的 UserManager 和 UserStore,然后按如下方式引导应用程序:

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

and inject both UserManager and UserStore into my Controller, without any problem:并将 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);
}

I came up with this:我想出了这个:

// Extract IApplicationUserManager interface with all methods you are using
public class ApplicationUserManager : UserManager<ApplicationUser>, IApplicationUserManager

// Register your custom user manager
services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddUserManager<ApplicationUserManager>();

// Return same scoped instance whenever you injecting your custom user manager into any constructor
services.AddScoped<IApplicationUserManager>(s => s.GetService<ApplicationUserManager>());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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