如何注入UserManager& SignInManager

时间:2016-03-26 19:16:41

标签: c# asp.net-mvc-5 entity-framework-6 ninject asp.net-identity

我想弄清楚如何注入UserManager和SignInManager。我在我的应用程序中安装了Ninject,并按以下方式使用它:

请将此视为一个全新的项目。在Startup.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;
    }
}

现在如果我要创建一个Dummy类并尝试根据其工作的接口注入它。我测试了它。我想弄清楚的是,我现在将如何从Startup.Auth.cs中删除以下内容并将其注入。没有我可以依赖的接口,我不确定如何做到这一点:

app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

再说一次,我的问题是:如何实例化ApplicationUserManager和ApplicationSignInManager并将其注入我的控制器参数中。这是我试图将其注入的控制器:

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

但是有了这个我得到空引用错误

2 个答案:

答案 0 :(得分:22)

先决条件

使用MVC模板启动新的MVC5应用程序。这将安装所有必需的依赖项,并部署Startup.Auth.cs文件,其中包含Microsoft.AspNet.Identity的引导代码(它包括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自动注入ApplicationSignInManagerApplicationUserManager个实例。

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();
}

多数民众赞成。现在,您应该可以导航到“登录”或“注册”链接,并且将进行注入。

替代方法

我更喜欢使用代理方法来公开ApplicationSignInManagerApplicationUserManager实例的有限功能。然后我将此代理注入必要的控制器。它有助于从控制器中抽象出一些身份信息,从而使将来更容易更改。这不是一个新概念,无论你是否这样做取决于项目的大小和复杂性以及你想如何处理依赖项。所以好处是(实际上对于任何代理都是常见的):

  • 您可以从代码
  • 中抽象出一些依赖项
  • 您可以简化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 Identity框架

答案 1 :(得分:17)

要准确回答我的问题所说的内容,以下是代码和说明:

第1步: 创建自定义用户存储

public class ApplicationUserStore : UserStore<ApplicationUser>
{
    public ApplicationUserStore(ApplicationDbContext context)
        : base(context)
    {
    }
}

第2步: 更新ApplicationUserManager并将代码从Create方法移动到构造函数

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步: 更新Startup.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>();

现在我们的AccountController构造函数看起来像这样:

public AccountController(IUserManagerSegment userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)  
{
    _userManager = userManager;
    _signInManager = signInManager;
    _authManager = authManager;
}

应该对SignInManager和AuthenticationManager做同样的事情。

上面的代码已经过测试并且正在运行。只需确保您引用了以下DLL:

Ninject.dll
Ninject.Web.Common
Ninject.Web.Common.OwinHost
Ninject.Web.Mvc