c# - que - La dependencia de la inyección de UserStore en el inicio de OWIN utilizando el middleware Ninject OWIN
que es owin c# (2)
Para información:
Es posible registrar el kernel como un singleton para que el middleware ninject pueda usar el mismo kernel y también se registre en el contexto de 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;
}
La función callback app.CreatePerOwinContext (ApplicationUserManager.Create) llamará a ApplicationUserManager.Create en lugar de registrarlo para que se llame más tarde durante la configuración. Por lo tanto, la función CreateKernel debe registrarse antes de la devolución de llamada Create de ApplicationUserManager o obtendrá una excepción de referencia nula si intenta obtener el kernel del contexto owin dentro de ese método.
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(CreateKernel);
app.UseNinjectMiddleware(CreateKernel);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
Esto le permitirá acceder al kernel para crear un UserStore personalizado dentro de la devolución de llamada Crear del UserUserManager:
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);
//...
}
Sé que, en general, la inyección de dependencia se debe favorecer sobre la ubicación del servicio, pero en este contexto no puedo ver una forma de evitarlo, ¿a menos que alguien tenga alguna sugerencia mejor?
Esto le permitirá usar Ninject para implementar el patrón de unidad de trabajo que aprovecha el InRequestScope () de Ninject. Funcionalidad de activación de OnDeactivation. Soy consciente de que la clase UserManager tiene una vida útil por solicitud , pero no conocía la forma más adecuada de realizar transacciones pendientes cuando finaliza la solicitud.
Estoy teniendo problemas para crear un UserStore personalizado usando la inyección de dependencia al crear un ApplicationUserManager usando el canal de solicitud OWIN.
Fondo
Estoy tratando de migrar la funcionalidad del usuario en nuestra aplicación web de usar SimpleMembership a la nueva identidad ASP.NET. Al iniciar un nuevo proyecto MVC 5, la implementación predeterminada de la aplicación de una sola página utiliza ASP.Identity, utilizando Entity Framework para implementar la funcionalidad UserStore.
En mi caso, ya estamos usando NHibernate como ORM, y estamos usando ninject para implementar el patrón de unidad de trabajo de modo que tengamos una sesión de NHibernate por solicitud, y quería hacer que el ASP.Identity funcione con nuestro marco existente.
Para este fin, creé un UserStore personalizado, que podría crearse inyectando los repositorios relevantes / sesión de inhibición, etc. Esto podría luego inyectarse en el constructor del Controlador usando Ninject, en lugar de usar la funcionalidad GetOwinContext de la implementación predeterminada.
Para hacer esto, comenté la siguiente línea en el método ConfigureAuth (aplicación IAppBuilder) del inicio, que de forma predeterminada crea la clase UserManager:
// app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
En su lugar, utilicé el NinjectWebCommon creado al instalar el paquete de nuget Ninject.Web.Common.Webhost para crear los enlaces relevantes.
Esta implementación funcionó bien con algunas de las operaciones de UserManager, pero con algunas operaciones, como ResetPasswordAsync, falla porque no se llama a la implementación predeterminada de ApplicationUserManager, por lo que el UserTokenProvider en la clase UserManager nunca se establece:
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;
}
Por lo tanto, el UserTokenProvider no está configurado.
Problema
Quiero usar la canalización OWIN, porque la implementación predeterminada de Visual Studio de la clase ApplicationUserManager inyecta el IDataProtectionProvider en su método Crear devolución de llamada. Sin embargo, también quiero crear mi UserStore utilizando la inyección de dependencia, y no sé cómo crear un UserStore dentro de este método utilizando la inyección de dependencia.
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);
}
He intentado evitar esta limitación usando el paquete de nuget Ninject.Web.Common.OwinHost y creando el kernel dentro de la clase de inicio.
public void ConfigureAuth(IAppBuilder app)
{
// Setup
app.UseNinjectMiddleware(CreateKernel);
}
Sin embargo, el Ninject.Web.Common.OwinHost no expone su Kernel, por lo que no puedo usar el patrón de ubicación del servicio para inyectar los valores en mi UserStore personalizado en Crear devolución de llamada.
También he intentado crear un Kernel de singleton y registrarlo usando app.CreatePerOwinContext (CreateKernel) con el delegado correspondiente, para poder acceder más tarde al Kernel, pero cuando llamo context.Get () simplemente devuelve un valor nulo.
Pregunta
¿Cómo puedo registrar una función de devolución de llamada con CreatePerOwinContext para crear un UserManager personalizado que usa un UserStore personalizado, y luego usar Ninject para crear el UserStore personalizado mediante la inyección de dependencia en la devolución de llamada Crear, de modo que también tenga acceso a IdentityFactoryOptions a las que usa Owin? ¿Inyectar el proveedor de token de usuario?
Nota Esto fue para WebApi (usando System.Web.Http
)
De acuerdo, en cierto modo hice trampa al usar cosas de System.Web
que es el espacio de nombres del que debemos suponer que nos estamos ocultando, pero mientras todavía se usa, ¿por qué no?
En primer lugar, utilizo algunos ayudantes de esta pregunta SO:
Configurando Ninject con Asp.Net MVC y Web Api
Dentro de la cual, el resolvedor está registrado con la configuración global de System.Web
. Por lo tanto, solo voy a agarrarlo cuando lo necesito
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);
...
Nota: uso el término Repositorio en Tienda, ya que coincide con el patrón conocido, más comprensible para la mayoría de las personas.
Y el Startup.Auth se ve así, básicamente muevo el inicio de Ninject aquí para que se haga a tiempo:
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);
...
También IKernel
un método similar al OP en el que " IKernel
" una devolución de llamada para obtener el IKernel
pero aunque esto lo mantiene todo en OWINy, el problema con este enfoque es que debe llamar a owinContextThing.Get<IKernel>()
Haciendo referencia a Ninject más profundo en mi código.
Había formas de evitarlo, pero comenzó a ser más complejo que mi solución anterior.
Nota adicional
Este es el código de Identity Framework que registra la devolución de llamada. Tenga en cuenta la llamada a app.GetDataProtectionProvider
que es esencialmente lo que originalmente necesitábamos para hacer un UserTokenProvider
.
/// <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;
}
¡He mirado y mirado y reflejado las libs y no puedo encontrar ese método! Si supiera cómo funcionó, podríamos encontrar otra forma de crear un token, es decir, no necesitaríamos la instancia de opciones.