在使用Ninject OWIN中间件的OWIN启动中dependency injectionUserStore

使用OWIN请求pipe道创buildApplicationUserManager时,使用dependency injection创build自定义UserStore时遇到问题。

背景

我正在尝试将我们的Web应用程序中的用户function从使用SimpleMembership迁移到新的ASP.NET标识。 当启动一个新的MVC 5项目时,单页应用程序的默认实现使用ASP.Identity,使用Entity Framework来实现UserStorefunction。

在我的例子中,我们已经使用NHibernate作为ORM,并且使用ninject来实现工作模式单元,以便每个请求有一个NHibernate会话,并且我想让ASP.Identity和我们现有的框架一起工作。

为此,我创build了一个自定义的UserStore,可以通过注入相关的存储库/ nhibernate会话等来创build。然后可以使用Ninject注入到Controller的构造函数中,而不是使用默认实现的GetOwinContextfunction。

为了做到这一点,我在Startup的ConfigureAuth(IAppBuilder应用程序)方法中注释了以下行,默认情况下它会创buildUserManager类:

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

相反,我使用在安装Ninject.Web.Common.Webhost nuget包时创build的NinjectWebCommon来创build相关的绑定。

这个实现对一些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没有设置。

问题

我想要使​​用OWINpipe道,因为Visual Studio的ApplicationUserManager类的默认实现在Createcallback方法中注入IDataProtectionProvider。 但是,我也想创build我的UserStore使用dependency injection,我不知道如何创build一个UserStore使用dependency injection此方法。

  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.Web.Common.OwinHost nuget包并在Startup类中创build内核来解决此限制。

  public void ConfigureAuth(IAppBuilder app) { // Setup app.UseNinjectMiddleware(CreateKernel); } 

但是,Ninject.Web.Common.OwinHost不公开其内核,所以我无法使用服务位置模式将值注入到Createcallback中的自定义UserStore中。

我也尝试创build一个单例内核,并使用app.CreatePerOwinContext(CreateKernel)与相关的委托注册,所以我可以稍后访问内核,但是当我调用context.Get()时,它只是返回null。

我怎样才能注册一个callback函数与CreatePerOwinContext创build一个自定义UserManager使用自定义UserStore,然后使用Ninject创build自定义UserStore使用dependency injection创buildcallback,这样我也有权访问Owin使用的IdentityFactoryOptions注入用户令牌提供者?

有关信息:

可以将内核注册为单例,以便ninject中间件可以使用同一个内核,并且也可以在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; } 

callback函数app.CreatePerOwinContext(ApplicationUserManager.Create)将调用ApplicationUserManager.Create,而不是注册它在稍后的安装过程中被调用。 因此,CreateKernel函数需要在ApplicationUserManager的Createcallback之前进行注册,否则,如果您尝试从该方法中的owin上下文获取内核,将会得到空引用exception。

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

这将允许您访问内核在ApplicationUserManager的Createcallback中创build一个自定义的UserStore:

  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); //... } 

我知道,一般情况下,dependency injection应该比服务位置更受欢迎,但在这种情况下,我看不到解决办法 – 除非有人有更好的build议?

这将允许您使用Ninject来实现工作模式,利用Ninject的InRequestScope()。OnDeactivationfunction。 我知道UserManager类有一个每个请求的生命周期 ,但不知道在请求完成时提交任何未完成事务的最合适的方法。

注意这是为WebApi(使用System.Web.Http

好吧,所以我使用System.Web中的东西来欺骗我们,这个名字空间是我们想要的,但是它仍然被使用,为什么呢?

首先,我从这个SO问题中使用一些帮助者:

用Asp.Net MVC&Web ApiconfigurationNinject

其中,parsing器注册System.Web的全局configuration。 因此,我只是在需要的时候抓住它:

  public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) { var repository = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver .GetService(typeof(Data.Repositories.UserRepository)) as Data.Repositories.UserRepository; var manager = new ApplicationUserManager(repository); ... 

注意:因为它与知名模式相匹配,所以我使用“存储库超越存储”一词,这对大多数人来说更容易理解。

Startup.Auth看起来像这样,我基本上把Ninject init移到这里,所以它在时间上完成:

  public void ConfigureAuth(IAppBuilder app) { // Dependency Injection Evoq.AppName.Configuration.Ninject.NinjectHttpContainer.RegisterAssembly(); // Configure the db context and user manager to use a single instance per request app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); ... 

我也使用了一种类似于OP的方法,在这个方法中,我附加了一个callbackIKernel来获取IKernel但是这个方法保留了OWINy,这个方法的问题是你必须调用owinContextThing.Get<IKernel>()在我的代码中更深入地引用Ninject。

有办法,但它开始变得比我上面的解决scheme更复杂。

附加说明

这是注册callback的身份框架代码。 请注意对app.GetDataProtectionProvider的调用,这本质上是我们最初创buildUserTokenProvider所需的东西。

  /// <summary> /// Registers a callback that will be invoked to create an instance of type T that will be stored in the OwinContext which can fetched via context.Get /// </summary> /// <typeparam name="T"></typeparam> /// <param name="app"></param> /// <param name="createCallback"></param> /// <returns></returns> public static IAppBuilder CreatePerOwinContext<T>(this IAppBuilder app, Func<IdentityFactoryOptions<T>, IOwinContext, T> createCallback) where T : class,IDisposable { if (app == null) { throw new ArgumentNullException("app"); } if (createCallback == null) { throw new ArgumentNullException("createCallback"); } app.Use(typeof(IdentityFactoryMiddleware<T, IdentityFactoryOptions<T>>), new IdentityFactoryOptions<T>() { DataProtectionProvider = app.GetDataProtectionProvider(), Provider = new IdentityFactoryProvider<T>() { OnCreate = createCallback } }); return app; } 

我看了看,反映了库,并找不到这种方法! 如果我知道如何工作,可能我们可以find另一种方法来创build一个令牌,即不需要选项实例。