如何在 DI 中注册自定义 UserStore 和 UserManager
How to register custom UserStore & UserManager in 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)
有人知道我做错了什么吗?
DI 通常用于接口驱动开发; .AddUserManager<ApplicationUserManager>()
指定一个实现 UserManager<>
,而不是服务接口。这意味着它仍然希望您获得 UserManager<ApplicationUser>
并且只能那样使用它;它会给你一个 ApplicationUserManager
.
我假设您有其他方法想在 ApplicationUserManager
上使用。 如果没有, 按照它的工作方式使用依赖构造函数,享受接口驱动的开发。如果是,你有 3 个选择:
通过组合而不是继承使用扩展。而不是从 UserManager<>
继承,而是将 ApplicationUserManager
写为包装器 class;您可以将其包含在构造函数中。这应该为您提供 ApplicationUserManager
.
中所需的所有功能
自己将其按原样添加到 DI 框架中。 这并不像听起来那么难,因为 UserManager<>
没有真实状态本身:
services.AddScoped<ApplicationUserManager>();
这里的缺点是您实际上有两个 UserManager<>
用户范围的对象;结果可能会导致效率低下。从目前代码的状态来看,我认为不是。
将其写为扩展方法。 如果您有许多依赖项而不仅仅是 UserManager<>
的基本功能,这可能是真的很复杂。
我现在使用 ASP.NET Core 1.1,此行为已得到修复。
我可以轻松实现自己的 UserManager 和 UserStore,然后 bootstrap 应用程序如下:
// 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);
}
我想到了这个:
// 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>());
这是我的设置:
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)
有人知道我做错了什么吗?
DI 通常用于接口驱动开发; .AddUserManager<ApplicationUserManager>()
指定一个实现 UserManager<>
,而不是服务接口。这意味着它仍然希望您获得 UserManager<ApplicationUser>
并且只能那样使用它;它会给你一个 ApplicationUserManager
.
我假设您有其他方法想在 ApplicationUserManager
上使用。 如果没有, 按照它的工作方式使用依赖构造函数,享受接口驱动的开发。如果是,你有 3 个选择:
通过组合而不是继承使用扩展。而不是从
UserManager<>
继承,而是将ApplicationUserManager
写为包装器 class;您可以将其包含在构造函数中。这应该为您提供ApplicationUserManager
. 中所需的所有功能
自己将其按原样添加到 DI 框架中。 这并不像听起来那么难,因为
UserManager<>
没有真实状态本身:services.AddScoped<ApplicationUserManager>();
这里的缺点是您实际上有两个
UserManager<>
用户范围的对象;结果可能会导致效率低下。从目前代码的状态来看,我认为不是。将其写为扩展方法。 如果您有许多依赖项而不仅仅是
UserManager<>
的基本功能,这可能是真的很复杂。
我现在使用 ASP.NET Core 1.1,此行为已得到修复。
我可以轻松实现自己的 UserManager 和 UserStore,然后 bootstrap 应用程序如下:
// 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);
}
我想到了这个:
// 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>());