我正在尝试弄清楚如何注入UserManager和SignInManager。 我已经在我的应用程序中安装了Ninject,我以以下方式使用它:
请考虑这是一个全新的项目。 在启动内部.cs我有以下内容:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.UseNinjectMiddleware(CreateKernel);
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}
}
现在,如果我要创建一些虚拟类并尝试根据其有效的接口注入它。 我已经测试过了。 我试图弄清楚的是,我现在如何从Startup.Auth中删除以下内容.cs并注入它。 没有我可以依赖的接口,我不确定这是如何完成的:
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
再澄清一次,我的问题是:如何实例化应用程序用户管理器和应用程序登录管理器并将其注入到我的控制器参数中。 这是我尝试将其注入的控制器:
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
编辑:
这是我尝试过的:
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
kernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
kernel.Bind<UserManager<ApplicationUser>>().ToSelf();
return kernel;
}
但是有了这个,我得到空引用错误
先决条件
使用 MVC 模板启动新的 MVC5 应用程序。这将安装所有必要的依赖项,并部署包含Microsoft.AspNet.Identity
引导代码的Startup.Auth.cs
文件(它包括Microsoft.AspNet.Identity的所有引用(。
安装以下软件包,然后将它们更新到最新版本。
Install-Package Ninject
Install-Package Ninject.MVC5
配置
删除AccountController
上的默认构造函数,以便仅保留参数化构造函数。它应该有以下签名。
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
这将确保在注入失败时会出现错误,这是我们想要的。
NInject 配置
NInject NuGet 包部署将创建一个名为 NinjectWebCommon.cs
的文件,其中进行样板 NInject 注册。这有一个具有以下签名的方法,您可以通过注册进行扩展。
private static void RegisterServices(IKernel kernel)
现在我们将向此方法添加以下代码,以使 NInject 自动注入 ApplicationSignInManager
和ApplicationUserManager
实例。
private static void RegisterServices(IKernel kernel) {
kernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
kernel.Bind<UserManager<ApplicationUser>>().ToSelf();
kernel.Bind<HttpContextBase>().ToMethod(ctx => new HttpContextWrapper(HttpContext.Current)).InTransientScope();
kernel.Bind<ApplicationSignInManager>().ToMethod((context)=>
{
var cbase = new HttpContextWrapper(HttpContext.Current);
return cbase.GetOwinContext().Get<ApplicationSignInManager>();
});
kernel.Bind<ApplicationUserManager>().ToSelf();
}
就是这样。现在您应该能够导航到登录或注册链接,并且将进行注入。
替代方法
我更喜欢为ApplicationSignInManager
和ApplicationUserManager
实例公开有限功能的代理方法。然后,我将此代理注入必要的控制器。它有助于从控制器中抽象出一些标识信息,以便将来更容易更改。这绝不是一个新概念,是否这样做取决于项目的大小和复杂性以及您希望如何处理依赖项。所以好处是(实际上对于任何代理来说都是常见的(:
- 您可以从代码中抽象出一些依赖项
- 您可以简化 API 中的一些调用
- 您只能公开要使用的功能,包括可配置部件
- 如果接口发生更改,更改管理应该更容易,现在您可以更改代理中的调用,而不是跨控制器的所有调用代码。
代码示例:
public interface IAuthManager
{
Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe);
}
public class AuthManager : IAuthManager
{
private ApplicationUserManager _userManager;
ApplicationSignInManager _signInManager;
public AuthManager(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
this._userManager = userManager;
this._signInManager = signInManager;
}
public Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe)
{
return _signInManager.PasswordSignInAsync(userName, password, rememberMe, true);
}
}
在 NInject 依赖项注册中添加以下行。
kernel.Bind<IAuthManager>().To<AuthManager>();
更改AccountController
构造函数以接受IAuthManager
的实例。最后,更改方法以直接引用此代理而不是 ASP.NET Identity 类。
免责声明 - 我没有接一个复杂的电话,只是一个非常简单的电话来说明我的观点。这也是完全可选的,是否这样做实际上应该取决于项目的范围和大小以及您计划如何使用 ASP.NET 标识框架
为了准确回答我的问题,以下是代码和说明:
第 1 步:创建自定义用户存储
public class ApplicationUserStore : UserStore<ApplicationUser>
{
public ApplicationUserStore(ApplicationDbContext context)
: base(context)
{
}
}
第 2 步:更新应用程序用户管理器并将代码从创建方法移动到构造函数
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store, IdentityFactoryOptions<ApplicationUserManager> options)
: base(store)
{
this.UserValidator = new UserValidator<ApplicationUser>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
this.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
this.UserLockoutEnabledByDefault = true;
this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
this.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two-factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
this.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
this.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
this.EmailService = new EmailService();
this.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
this.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
}
}
第 3 步:修改 Startup.Auth 类并注释掉以下代码
//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
//app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
第 4 步:更新帐户控制器(或任何其他有问题的控制器(并添加以下构造函数
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)
{
_userManager = userManager;
_signInManager = signInManager;
_authManager = authManager;
}
第 5 步:更新帐户控制器并使属性只能检索,如下所示:
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager;
}
}
private IAuthenticationManager AuthenticationManager
{
get
{
return _authManager;
}
}
第 6 步:更新启动.cs
public partial class Startup
{
private IAppBuilder _app;
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
_app = app;
app.UseNinjectMiddleware(CreateKernel);
}
private IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
kernel.Bind<ApplicationDbContext>().ToSelf().InRequestScope();
kernel.Bind<IUserStore<ApplicationUser>>().To<ApplicationUserStore>();
kernel.Bind<ApplicationUserManager>().ToSelf();
kernel.Bind<ApplicationSignInManager>().ToSelf();
kernel.Bind<IAuthenticationManager>().ToMethod(x => HttpContext.Current.GetOwinContext().Authentication);
kernel.Bind<IDataProtectionProvider>().ToMethod(x => _app.GetDataProtectionProvider());
return kernel;
}
}
为了进一步扩展这个问题的答案,根据我收到的评论:
不应将这些管理器作为类注入,因为这样您就不会完成 DI。 相反,应该做的是创建多个接口,根据您的需要进一步分离和分组UserManager的方法。 下面是一个示例:
public interface IUserManagerSegment
{
Task<IdentityResult> CreateAsync(ApplicationUser user, string password);
Task<IdentityResult> CreateAsync(ApplicationUser user);
Task<IdentityResult> ConfirmEmailAsync(string userId, string token);
Task<ApplicationUser> FindByNameAsync(string userName);
Task<bool> IsEmailConfirmedAsync(string userId);
Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword);
Task<IList<string>> GetValidTwoFactorProvidersAsync(string userId);
Task<IdentityResult> AddLoginAsync(string userId, UserLoginInfo login);
void Dispose(bool disposing);
void Dispose();
}
上面的方法列出了我选择的几种随机方法,只是为了说明这一点。 话虽如此,我们现在将基于接口注入方法,如下所示:
kernel.Bind<IUserManagerSegment>().To<ApplicationUserManager>();
现在我们的帐户控制器构造函数如下所示:
public AccountController(IUserManagerSegment userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)
{
_userManager = userManager;
_signInManager = signInManager;
_authManager = authManager;
}
对 SignInManager 和 AuthenticationManager 也应该执行相同的操作。
上面的代码已经过测试并正在运行。 只需确保已引用以下 DLL:
宁注射.dll
Ninject.Web.Common
Ninject.Web.Common.OwinHost
Ninject.Web.Mvc