OWIN要求パイプラインを使用してApplicationUserManagerを作成するときに、依存関係注入を使用してカスタムUserStoreを作成すると問題が発生します。
背景
SimpleMembershipの使用から新しいASP.NET IdentityにWebアプリケーションのユーザー機能を移行しようとしています。新しいMVC 5プロジェクトを開始するとき、シングルページアプリケーションのデフォルトの実装では、ASP.Identityを使用し、Entity Frameworkを使用してUserStore機能を実装します。
私の場合、すでにNHibernateをORMとして使用しており、ninjectを使用して作業単位パターンを実装しているため、リクエストごとに1つのNHibernateセッションがあり、ASP.Identityを既存のフレームワークで動作させたいと考えていました。
この目的のために、関連するリポジトリ/ nhibernateセッションなどを注入することで作成できるカスタムUserStoreを作成しました。これは、デフォルト実装のGetOwinContext機能を使用するのではなく、Ninjectを使用してコントローラーのコンストラクターに挿入できます。
これを行うために、デフォルトでUserManagerクラスを作成するStartupのConfigureAuth(IAppBuilderアプリ)メソッドの次の行をコメントアウトしました。
// app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
代わりに、Ninject.Web.Common.Webhost nugetパッケージのインストール時に作成されたNinjectWebCommonを使用して、関連するバインディングを作成しました。
この実装はいくつかの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は設定されていません。
問題
Visual StudioのApplicationUserManagerクラスのデフォルト実装は、CreateコールバックメソッドにIDataProtectionProviderを注入するため、OWINパイプラインを使用します。ただし、依存性注入を使用して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.Web.Common.OwinHost nugetパッケージを使用し、Startupクラス内にカーネルを作成して、この制限を回避しようとしました。
public void ConfigureAuth(IAppBuilder app)
{
// Setup
app.UseNinjectMiddleware(CreateKernel);
}
ただし、Ninject.Web.Common.OwinHostはそのカーネルを公開しないため、Create Locationコールバックでサービスロケーションパターンを使用してカスタムUserStoreに値を注入することはできません。
また、シングルトンカーネルを作成し、関連するデリゲートでapp.CreatePerOwinContext(CreateKernel)を使用してこれを登録しようとしたため、後でカーネルにアクセスできますが、context.Get()を呼び出すと、nullが返されます。
質問
CreatePerOwinContextでコールバック関数を登録して、カスタムUserStoreを使用するカスタムUserManagerを作成し、Ninjectを使用してCreateコールバックで依存性注入を使用してカスタムUserStoreを作成し、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;
}
コールバック関数app.CreatePerOwinContext(ApplicationUserManager.Create)は、セットアップ中に後で呼び出されるように登録するのではなく、ApplicationUserManager.Createを呼び出します。したがって、ApplicationUserManagerのCreateコールバックの前にCreateKernel関数を登録する必要があります。登録しないと、そのメソッド内のowinコンテキストからカーネルを取得しようとすると、null参照例外が発生します。
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(CreateKernel);
app.UseNinjectMiddleware(CreateKernel);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
これにより、カーネルにアクセスして、ApplicationUserManagerのCreateコールバック内でカスタム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);
//...
}
一般に、依存関係の注入はサービスの場所よりも優先されるべきであることを知っていますが、このコンテキストでは、それを回避する方法を見つけることができませんでした。
これにより、Ninjectを使用して、NinjectのInRequestScope()。OnDeactivation機能を活用した作業ユニットパターンを実装できます。 UserManagerクラスには リクエストライフタイムごと がありますが、リクエストの終了時に未処理のトランザクションをコミットする最も適切な方法を知りませんでした。