在使用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一个令牌,即不需要选项实例。
- 如何在ASP.NET中获取客户端date和时间?
- entity framework刷新上下文?
- 获取url,而不querystring
- 如何设置环境名称(IHostingEnvironment.EnvironmentName)?
- 从URL下载/stream文件 – asp.net
- 当从Web上调用存储过程时,速度很慢,来自Management Studio
- 如何将NameValueCollection转换为JSONstring?
- 控制台应用程序:每个configuration文件只允许有一个<configSections>元素,如果存在,则必须是根<configuration>元素的第一个子元素
- RegisterStartupScript和RegisterClientScriptBlock之间的区别?