How to register custom UserStore & UserManager in

2020-02-23 07:44发布

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

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

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:

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

And here is my 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?

3条回答
乱世女痞
2楼-- · 2020-02-23 08:20

DI in general is intended for interface-driven development; .AddUserManager<ApplicationUserManager>() specifies an implementation UserManager<>, not the service interface. That means that it's still expecting you to get UserManager<ApplicationUser> and only use it that way; it'll give you an ApplicationUserManager.

I'm assuming that you have additional methods you want to use on your ApplicationUserManager. If not, just use the dependency constructor the way it works and enjoy the interface-driven development. If so, you have 3 options:

  1. Use extension via composition rather than inheritance. Rather than inheriting from UserManager<>, write ApplicationUserManager as a wrapper class; you can include it in the constructor. This should give you all the functionality you need inside of the ApplicationUserManager.

  2. Add it as-is to the DI framework yourself. This isn't as difficult as it sounds, since the UserManager<> has no real state itself:

    services.AddScoped<ApplicationUserManager>();
    

    The disadvantage here is that you'll actually have two UserManager<> objects for the user's scope; 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.

查看更多
相关推荐>>
3楼-- · 2020-02-23 08:20

I am now on ASP.NET Core 1.1 and this behavior has been fixed.

I can easily implement my own UserManager and UserStore, then bootstrap the app as following:

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

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);
}
查看更多
Explosion°爆炸
4楼-- · 2020-02-23 08:26

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>());
查看更多
登录 后发表回答