如何注入UserManager和SignInManager

Boj*_*jan 18 c# ninject entity-framework-6 asp.net-mvc-5 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
Run Code Online (Sandbox Code Playgroud)

为了澄清一次,我的问题是:如何实例化ApplicationUserManager和ApplicationSignInManager并将其注入我的控制器参数中.这是我试图将其注入的控制器:

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
    UserManager = userManager;
    SignInManager = signInManager;
}
Run Code Online (Sandbox Code Playgroud)

编辑:

这是我尝试过的:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

Igo*_*gor 23

先决条件

使用MVC模板启动新的MVC5应用程序.这将安装所有必需的依赖项,并部署Startup.Auth.cs包含引导代码的文件Microsoft.AspNet.Identity(它包括Microsoft.AspNet.Identity的所有引用).

安装以下软件包,然后将其更新到最新版本.

Install-Package Ninject
Install-Package Ninject.MVC5
Run Code Online (Sandbox Code Playgroud)

组态

删除默认构造函数,AccountController只保留参数化构造函数.它应该有followigig签名.

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
Run Code Online (Sandbox Code Playgroud)

这将确保如果注入失败,您将收到错误,这是我们想要的.

NInject配置

NInject NuGet包部署将创建一个名为NinjectWebCommon.cs锅炉板NInject注册的文件.这有一个带有以下签名的方法,您可以随注册一起扩展.

private static void RegisterServices(IKernel kernel)
Run Code Online (Sandbox Code Playgroud)

现在我们将向此方法添加以下代码,以使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();
}
Run Code Online (Sandbox Code Playgroud)

而已.现在您应该能够导航到登录或注册链接并进行注入.

替代方法

我更喜欢一种代理方法,它为实例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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在NInject依赖项注册中添加以下行.

kernel.Bind<IAuthManager>().To<AuthManager>();
Run Code Online (Sandbox Code Playgroud)

改变你的AccountController构造函数来接受一个实例IAuthManager.最后更改您的方法以直接引用此代理而不是ASP.NET Identity类.

免责声明 - 我没有连接复杂的电话,只是一个非常简单的电话来说明我的观点.这也是完全可选的,无论你是否真的这样做,都应该取决于项目的范围和大小以及你计划如何使用ASP.NET Identity框架


Boj*_*jan 18

为了准确回答我的问题,这里是代码和说明:

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

public class ApplicationUserStore : UserStore<ApplicationUser>
{
    public ApplicationUserStore(ApplicationDbContext context)
        : base(context)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

第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"));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

步骤3:修改Startup.Auth类并注​​释掉以下代码

//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
//app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
Run Code Online (Sandbox Code Playgroud)

步骤4:更新帐户控制器(或任何其他有问题的控制器)并添加以下构造函数

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _authManager = authManager;
}
Run Code Online (Sandbox Code Playgroud)

步骤5:更新帐户控制器并使属性仅可以恢复,如下所示:

public ApplicationSignInManager SignInManager
{
    get
    {
        return _signInManager;
    }
}

public ApplicationUserManager UserManager
{
    get
    {
        return _userManager;
    }
}

private IAuthenticationManager AuthenticationManager
{
    get
    {
        return _authManager;
    }
}
Run Code Online (Sandbox Code Playgroud)

第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;
    }
}
Run Code Online (Sandbox Code Playgroud)

根据我收到的评论,进一步扩大这个问题的答案:

这些经理不应该被注入课程,因为你没有完成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();
}
Run Code Online (Sandbox Code Playgroud)

上面的方法列出了我选择的几个随机方法来说明这一点.有了这个说,我们现在将基于接口注入方法,例如:

kernel.Bind<IUserManagerSegment>().To<ApplicationUserManager>();
Run Code Online (Sandbox Code Playgroud)

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

public AccountController(IUserManagerSegment userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)  
{
    _userManager = userManager;
    _signInManager = signInManager;
    _authManager = authManager;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 很好,看到您在我发布解决方案时正确​​发布了此信息。我使用 VS 2015 使用新设置测试了所有内容。如果您还有任何问题或需要查看其他内容,请告诉我? (2认同)