在使用OWIN请求管道创建ApplicationUserManager时,我遇到了使用依赖项注入创建自定义UserStore的问题.

Background

我正在try 将web应用程序中的用户功能从使用SimpleMembership迁移到新的ASP.网络身份.启动新的MVC 5项目时,单页应用程序的默认实现使用ASP.身份,使用实体框架实现UserStore功能.

在我的例子中,我们已经使用NHibernate作为ORM,并使用ninject来实现工作单元模式,这样每个请求都有一个NHibernate会话,我想让ASP.身份识别与我们现有的框架一起工作.

为此,我创建了一个自定义的UserStore,可以通过注入相关的存储库/nhibernate会话等来创建它.然后可以使用NINJECT将其注入到控制器的构造函数中,而不是使用默认实现的GetOwinContext功能.

为此,我注释掉了Startup的ConfigureAuth(IAppBuilder应用程序)方法中的以下行,该方法默认创建UserManager类:

// app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

相反,我使用了安装Ninject时创建的NinjectWebCommon.网状物常见的Webhost nuget包创建相关绑定.

此实现在某些UserManager操作中运行良好,但在某些操作(如ResetPasswordAsync)中,它会失败,因为未调用默认的ApplicationUserManager实现,因此从未设置UserManager类中的UserTokenProvider:

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };
        // Configure validation logic for passwords
        manager.PasswordValidator = new PasswordValidator
        {
            RequiredLength = 6,
            RequireNonLetterOrDigit = true,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        };
        // 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 in here.
        manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
        {
            MessageFormat = "Your security code is: {0}"
        });
        manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
        {
            Subject = "Security Code",
            BodyFormat = "Your security code is: {0}"
        });
        manager.EmailService = new EmailService();
        manager.SmsService = new SmsService();
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }
        return manager;
    }

因此,未设置UserTokenProvider.

问题

我想使用OWIN管道,因为Visual Studio的ApplicationUserManager类的默认实现在其创建回调方法中注入了IDataProtectionProvider.然而,我也想使用依赖注入创建我的UserStore,我不知道如何使用依赖注入在这个方法中创建UserStore.

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
        // WANT TO CREATE THE USER STORE USING NINJECT DEPENDENCY INJECTION HERE
        // var userStore = ...
        var manager = new ApplicationUserManager(userStore);
    }

我试图通过使用Ninject来绕过这个限制.网状物常见的OwinHost nuget包,并在Startup类中创建内核.

    public void ConfigureAuth(IAppBuilder app)
    {
        // Setup

        app.UseNinjectMiddleware(CreateKernel);
    }

但是,Ninject.Web.Common.OwinHost不公开其内核,因此我无法使用服务位置模式在创建回调中将值注入到我的自定义UserStore中.

我还try 创建一个Singleton Kernel,并使用app.CreatePerOwinContext(CreateKernel)将其注册到相关委托,以便稍后可以访问该内核,但是当我调用context.Get()时,它只返回null.

Question

如何使用CreatePerOwinContext注册回调函数以创建使用自定义UserStore的自定义UserManager,然后在CREATE回调中使用依赖项注入使用NINJECT创建自定义UserStore,以便我还可以访问owin用来注入用户令牌提供程序的IdentityFactoryOptions?

推荐答案

有关信息:

可以将内核注册为单实例,从而相同的内核可以由对象中间件使用,并且还可以在OWIN上下文中注册.

    public static StandardKernel CreateKernel()
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel();
            _kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

            _kernel.Load(Assembly.GetExecutingAssembly(), Assembly.Load("Super.CompositionRoot"));
        }
        return _kernel;
    }

回调函数应用程序.CreatePerOwinContext(ApplicationUserManager.Create)将调用ApplicationUserManager.创建而不是注册它,以便在安装过程中稍后调用.因此,需要在ApplicationUserManager的创建回调之前注册CreateKernel函数,否则,如果试图从该方法内的owin上下文中获取内核,则会出现空引用异常.

    public void ConfigureAuth(IAppBuilder app)
    {
        app.CreatePerOwinContext(CreateKernel);
        app.UseNinjectMiddleware(CreateKernel);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
     }

这将允许您访问内核,在ApplicationUserManager的create回调中创建自定义用户存储:

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
        var kernel = context.Get<StandardKernel>();
        var userStore = kernel.Get<IUserStore<User, int>>();
        var manager = new ApplicationUserManager(userStore);
        //...
    }

我知道一般来说,依赖注入应该比服务位置更受欢迎,但在这种情况下,我看不到解决方法——除非有人有更好的建议?

这将允许您使用Ninject利用Ninject的InRequestScope()实现工作单元模式.激活功能.我知道UserManager类有一个per request lifetime,但不知道在请求完成时提交任何未完成事务的最合适方式.

Asp.net相关问答推荐

如何将标头中的用户名/密码传递给 SOAP WCF 服务

如何删除字符串的定义部分?

下载功能在 asp.net 的更新面板中不起作用

使用实体框架从存储过程中获取数据

SameSite 属性如何自动添加到我的 Asp.net_SessionID cookie 中?

如何将 Castle Windsor 与 ASP.Net Web 表单一起使用?

等待本地主机,永远!

从 GUID 中删除破折号是个好主意吗?

以编程方式添加跨度标签,而不是标签控件?

如何在 ASP.NET 中仅在调试模式下执行代码

如何解决 Kerberos 双跳问题?

每个 'HttpRequest' 在 ASP.NET 中都有自己的线程吗?

判断邮箱地址是否对 System.Net.Mail.MailAddress 有效

IIS 中的 existingResponse="PassThrough" 是什么意思?

页面在谷歌浏览器中加载两次

Owin.IAppBuilder不包含MapSignalR的定义

C# 7 本地函数未按预期工作且未显示错误

如何进入 IIS 管理器?

Windows 身份验证在本地 IIS 7.5 上不起作用.错误 401.1

jQuery Ajax 调用 - 成功设置变量值