web-dev-qa-db-ja.com

UserManagerとSignInManagerを注入する方法

UserManagerとSignInManagerを注入する方法を見つけようとしています。 Ninjectをアプリケーションにインストールし、次の方法で使用しています。

これは真新しいプロジェクトであると考えてください。 Startup.csの内部には次のものがあります。

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);

        app.UseNinjectMiddleware(CreateKernel);
    }

    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Load(Assembly.GetExecutingAssembly());


        return kernel;
    }
}

ダミークラスを作成し、動作するインターフェイスに基づいてそれを挿入しようとするとします。私はそれをテストしました。私が理解しようとしているのは、Startup.Auth.csから次のものを取り除き、それを注入する方法です。信頼できるインターフェイスがないため、これがどのように行われるかわかりません:

app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

もう一度明確にするために、私の質問は次のとおりです。ApplicationUserManagerとApplicationSignInManagerをインスタンス化し、コントローラーパラメーターに挿入する方法を教えてください。これを注入しようとしているコントローラーは次のとおりです。

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
    UserManager = userManager;
    SignInManager = signInManager;
}

編集:

ここに私が試したものがあります:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Load(Assembly.GetExecutingAssembly());

    kernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
    kernel.Bind<UserManager<ApplicationUser>>().ToSelf();

    return kernel;
}

しかし、これでnull参照エラーが発生します

17
Bojan

私の質問が述べたことに正確な答えを与えるために、ここにコードと指示があります:

ステップ1:カスタムユーザーストアを作成する

public class ApplicationUserStore : UserStore<ApplicationUser>
{
    public ApplicationUserStore(ApplicationDbContext context)
        : base(context)
    {
    }
}

ステップ2:ApplicationUserManagerを更新し、Createメソッドからコンストラクターにコードを移動します

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(IUserStore<ApplicationUser> store, IdentityFactoryOptions<ApplicationUserManager> options)
        : base(store)
    {
        this.UserValidator = new UserValidator<ApplicationUser>(this)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

        // Configure validation logic for passwords
        this.PasswordValidator = new PasswordValidator
        {
            RequiredLength = 6,
            RequireNonLetterOrDigit = true,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        };

        // Configure user lockout defaults
        this.UserLockoutEnabledByDefault = true;
        this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
        this.MaxFailedAccessAttemptsBeforeLockout = 5;

        // 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 it in here.
        this.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
        {
            MessageFormat = "Your security code is {0}"
        });
        this.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
        {
            Subject = "Security Code",
            BodyFormat = "Your security code is {0}"
        });
        this.EmailService = new EmailService();
        this.SmsService = new SmsService();
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            this.UserTokenProvider =
                new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }
    }
}

ステップ3:Startup.Authクラスを変更し、次のコードをコメントアウトします

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

手順4:アカウントコントローラー(または問題のコントローラー)を更新し、次のコンストラクターを追加します

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _authManager = authManager;
}

ステップ5:アカウントコントローラーを更新し、プロパティのみを取得可能にする:

public ApplicationSignInManager SignInManager
{
    get
    {
        return _signInManager;
    }
}

public ApplicationUserManager UserManager
{
    get
    {
        return _userManager;
    }
}

private IAuthenticationManager AuthenticationManager
{
    get
    {
        return _authManager;
    }
}

ステップ6:Startup.csを更新する

public partial class Startup
{
    private IAppBuilder _app;
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
        _app = app;
        app.UseNinjectMiddleware(CreateKernel);
    }

    private IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Load(Assembly.GetExecutingAssembly());

        kernel.Bind<ApplicationDbContext>().ToSelf().InRequestScope();
        kernel.Bind<IUserStore<ApplicationUser>>().To<ApplicationUserStore>();
        kernel.Bind<ApplicationUserManager>().ToSelf();
        kernel.Bind<ApplicationSignInManager>().ToSelf();
        kernel.Bind<IAuthenticationManager>().ToMethod(x => HttpContext.Current.GetOwinContext().Authentication);
        kernel.Bind<IDataProtectionProvider>().ToMethod(x => _app.GetDataProtectionProvider());

        return kernel;
    }
}

私が受け取ったコメントに基づいて、この質問に対する答えをさらに拡大するには:

これらのマネージャーは、DIを達成していないため、クラスとして注入しないでください。代わりに、必要に応じてUserManagerのメソッドをさらに分離およびグループ化する複数のインターフェイスを作成する必要があります。以下に例を示します。

public interface IUserManagerSegment
{
    Task<IdentityResult> CreateAsync(ApplicationUser user, string password);
    Task<IdentityResult> CreateAsync(ApplicationUser user);
    Task<IdentityResult> ConfirmEmailAsync(string userId, string token);
    Task<ApplicationUser> FindByNameAsync(string userName);
    Task<bool> IsEmailConfirmedAsync(string userId);
    Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword);
    Task<IList<string>> GetValidTwoFactorProvidersAsync(string userId);
    Task<IdentityResult> AddLoginAsync(string userId, UserLoginInfo login);
    void Dispose(bool disposing);
    void Dispose();
}

上記の方法には、ポイントを説明するために選んだいくつかのランダムな方法のリストがあります。これで、次のようなインターフェイスに基づいてメソッドを注入します。

kernel.Bind<IUserManagerSegment>().To<ApplicationUserManager>();

そして、AccountControllerコンストラクターは次のようになります。

public AccountController(IUserManagerSegment userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)  
{
    _userManager = userManager;
    _signInManager = signInManager;
    _authManager = authManager;
}

SignInManagerとAuthenticationManagerにも同じことを行う必要があります。

上記のコードはテスト済みで動作しています。次のDLLを参照していることを確認してください。

Ninject.dll
Ninject.Web.Common
Ninject.Web.Common.OwinHost
Ninject.Web.Mvc

18
Bojan

前提条件

MVCテンプレートを使用して、新しいMVC5アプリケーションを開始します。これにより、必要な依存関係がすべてインストールされ、Startup.Auth.csのbootstrap codeを含むMicrosoft.AspNet.Identityファイルがデプロイされます(Microsoft.AspNetのすべての参照が含まれます) 。身元)。

次のパッケージをインストールし、その後最新のものに更新します。

Install-Package Ninject
Install-Package Ninject.MVC5

構成

AccountControllerのデフォルトコンストラクターを削除して、パラメーター化されたコンストラクターのみが残るようにします。次の署名が必要です。

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)

これにより、インジェクションが失敗した場合にエラーが発生することが保証されます。

NInject設定

NInject NuGetパッケージの展開により、ボイラープレートのNInject登録が行われるNinjectWebCommon.csという名前のファイルが作成されます。これには、登録で拡張できる次の署名を持つメソッドがあります。

private static void RegisterServices(IKernel kernel)

次のコードをこのメソッドに追加して、NInjectがApplicationSignInManagerおよびApplicationUserManagerインスタンスを自動的に挿入するようにします。

private static void RegisterServices(IKernel kernel) {
    kernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
    kernel.Bind<UserManager<ApplicationUser>>().ToSelf();

    kernel.Bind<HttpContextBase>().ToMethod(ctx => new HttpContextWrapper(HttpContext.Current)).InTransientScope();

    kernel.Bind<ApplicationSignInManager>().ToMethod((context)=>
    {
        var cbase = new HttpContextWrapper(HttpContext.Current);
        return cbase.GetOwinContext().Get<ApplicationSignInManager>();
    });

    kernel.Bind<ApplicationUserManager>().ToSelf();
}

それでおしまい。これで、ログインまたは登録リンクに移動でき、インジェクションが発生します。

代替アプローチ

ApplicationSignInManagerおよびApplicationUserManagerインスタンスの制限された機能を公開するプロキシアプローチを好みます。次に、このプロキシを必要なコントローラーに注入します。これにより、一部のID情報をコントローラーから切り離すことができ、将来の変更が容易になります。これは決して新しい概念ではなく、これを行うかどうかは、プロジェクトのサイズと複雑さ、および依存関係の処理方法によって決まります。そのため、利点は次のとおりです(実際、すべてのプロキシに共通):

  • コードからいくつかの依存関係を抽象化できます
  • API内のいくつかの呼び出しを合理化できます
  • 構成可能なパーツなど、使用したい機能のみを公開できます
  • インターフェースが変更された場合、変更管理は簡単になります。コントローラー全体のすべての呼び出しコードではなく、プロキシの呼び出しを変更します。

コード例:

public interface IAuthManager
{
    Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe);
}

public class AuthManager : IAuthManager
{
    private ApplicationUserManager _userManager;
    ApplicationSignInManager _signInManager;

    public AuthManager(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
    {
        this._userManager = userManager;
        this._signInManager = signInManager;
    }

    public Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe)
    {
        return _signInManager.PasswordSignInAsync(userName, password, rememberMe, true);
    }
}

NInject依存関係登録に次の行を追加します。

kernel.Bind<IAuthManager>().To<AuthManager>();

AccountControllerコンストラクターを変更して、IAuthManagerのインスタンスを取り込みます。最後に、ASP.NET Identityクラスではなくこのプロキシを直接参照するようにメソッドを変更します。

免責事項-複雑な呼び出しは行いませんでした。ポイントを説明するための非常に簡単な呼び出しです。これも完全にオプションであり、実行するかどうかは、プロジェクトの範囲とサイズ、および方法によって異なります。 ASP.NET Identityフレームワークの使用を計画しています

23
Igor