c# tutorial 単純インジェクタでI認証マネージャを登録する




simpleinjector class library (3)

Simple Injectorのための構成設定を持っています。そこで私は私のすべての登録をOWINパイプラインに移しました。

今問題は私が実際にようにパラメータを取るコントローラAccountControllerを持っていることです

public AccountController(
    AngularAppUserManager userManager, 
    AngularAppSignInManager signinManager, 
    IAuthenticationManager authenticationManager)
{
    this._userManager = userManager;
    this._signInManager = signinManager;
    this._authenticationManager = authenticationManager;
}

今私のOwin Pipelineの設定はこのようになっています

public void Configure(IAppBuilder app)
{
    _container = new Container();
    ConfigureOwinSecurity(app);
    ConfigureWebApi(app);
    ConfigureSimpleinjector(_container);

    app.Use(async (context, next) =>
    {
        _container.Register<IOwinContext>(() => context);
        await next();
    });

    _container.Register<IAuthenticationManager>(
        () => _container.GetInstance<IOwinContext>().Authentication);

    _container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
}

private static void ConfigureOwinSecurity(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        CookieName = "AppNgCookie",
        //LoginPath = new PathString("/Account/Login")
    });
}

private static void ConfigureWebApi(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();
    WebApiConfig.Register(config);
    app.UseWebApi(config);
}

private static void ConfigureSimpleinjector(Container container)
{
    SimpleInjectorInitializer.Initialize(container);
}

そしてSimple Injector Initializerはこのように見えます

private static void InitializeContainer(Container container)
{
    container.Register<DbContext, AngularAppContext>();

    container.Register<IUserStore<Users, Guid>, AngularAppUserStore>();
    container.Register<IRoleStore<Roles, Guid>, AngularAppRoleStore>();

    container.Register<UserManager<Users, Guid>, AngularAppUserManager>();
    container.Register<RoleManager<Roles, Guid>, AngularAppRoleManager>();
    //container.RegisterPerWebRequest<SignInManager<Users, Guid>, AngularAppSignInManager>();

    container.Register<IdentityFactoryOptions<AngularAppUserManager>, IdentityFactoryOptions<AngularAppUserManager>>();
    //container.Register<IAuthenticationManager>(() => HttpContext.Current.GetOwinContext().Authentication);

    //container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
    // For instance:
    // container.Register<IUserRepository, SqlUserRepository>();
}

今問題はです。コントローラはIAuthenticationManagerを登録できません。 使ってみた

container.Register<IAuthenticationManager>(
    () => HttpContext.Current.GetOwinContext().Authentication);

しかし、それは例外として私を残します:

System.InvalidOperationException:コンテキスト内にowin.Environment項目が見つかりませんでした。

この行で

container.Register<IAuthenticationManager>(
    () => HttpContext.Current.GetOwinContext().Authentication);

HttpContext.Current.GetOwinContext().Authenticationを使う代わりに、 public void Configure(app)メソッドでapp.Use()を使って登録する上記の設定でHttpContext.Current.GetOwinContext().Authenticationしてみました。 そして、後でIAuthenticationManagerを取得するためにコンテナ経由でそれを解決します。 しかし、あらゆる可能性が私を失敗させた。

私はここで何が足りないのですか? なぜHttpContext.Current.GetOwinContext().AuthentcationがOwinContextからの認証を解決できないのですか?

そうでない場合は、なぜapp.Useを介して同じ設定がapp.Useないのでしょうか。


あなたがIAuthenticationManager登録でしていることは問題なく私のために働きました 。 ある時点で私はあなたが得ていたのと同じ例外を受けていましたが、それは以下の行によって引き起こされました。

container.Verify();

コンテナー構成の直後 登録済みオブジェクトのすべてのインスタンスを作成しようとしていましたが、 HttpContext.Currentが存在しなかっHttpContext.Currentため、例外が発生しました。

HTTPリクエストが利用可能になる前に、コンテナからインスタンスを取得していませんか。 本当に必要な場合は、NightOwl888で示唆されているように、これを回避する唯一の方法はFactoryを使うことです。 あなたがHTTPリクエストの前にコンテナを必要としないならば、それからリファクタリングしてください、それでそれはHTTPリクエストの外で使われません。


here私の答えを見てhere

あなたは別のタイプにアクセスしていますが、問題は同じです。 アプリケーションはユーザーのコンテキスト外で初期化されるため、アプリケーションの起動時にHttpContextのプロパティに依存することはできません。 解決策は、オブジェクトの作成時ではなく実行時に値を読み取り、IAuthenticationManagerタイプではなくファクトリをコントローラに挿入するように抽象ファクトリを作成することです。

public class AccountController
{
    private readonly AngularAppUserManager _userManager;
    private readonly AngularAppSignInManager _signInManager;
    private readonly IAuthenticationManagerFactory _authenticationManagerFactory;

    public AccountController(AngularAppUserManager userManager
      , AngularAppSignInManager signinManager
      , IAuthenticationManagerFactory authenticationManagerFactory)
    {
        this._userManager = userManager;
        this._signInManager = signinManager;
        this._authenticationManagerFactory = authenticationManagerFactory;
    }

    private IAuthenticationManager AuthenticationManager
    {
        get { return this._authenticationManagerFactory.Create(); }
    }

    private void DoSomething()
    {
        // Now it is safe to call into HTTP context
        var manager = this.AuthenticationManger;
    }
}

public interface IAuthenticationMangerFactory
{
    IAuthenticationManger Create();
}

public class AuthenticationMangerFactory
{
    public IAuthenticationManger Create()
    {
        HttpContext.Current.GetOwinContext().Authentication;
    }
}

// And register your factory...
container.Register<IAuthenticationManagerFactory, AuthenticationMangerFactory>();

TrailMaxがすでに述べたように、 container.Verify()呼び出し中におそらく発生した例外が発生します。 アプリケーションの起動時にはHttpContextがないため、例外があります。

container.Verify()への呼び出しを削除することで問題を「解決」することができますが、これをやめることをお勧めします。以下でより良い解決策を提案します。

NightOwl888はMark Seemannの古い記事を参照しています(私はDIに関する彼の仕事を非常に尊重しています)。 その記事の中で Markは、なぜコンテナーを検証するのは無駄だと思うのかを説明しています。 しかし、この記事は古くなっているようで、Markの新しい記事と矛盾しています。 最新の記事で Markは、 Pure DIを使用する大きな利点の1つ(つまりDIコンテナを使用せずにDependency Injectionを使用すること )が、 正しいことについて最速のフィードバックを提供できることを説明します 。 Markと私たちの残りの部分は、明らかに、迅速なフィードバックメカニズムとして、コンパイラのフィードバックと静的コード分析ツールからのフィードバックの両方を重視しています。 Simple Injectorの.Verify()Diagnostic Servicesはどちらもこの速いフィードバックを取り戻そうとします。 私の考えでは、Simple Injectorの.Verify()メソッドは、純粋なDIを実行するときにコンパイラがあなたに.Verify()て実行する仕事を引き受けます。そして、Diagnostic Servicesは、ある意味ではあなたのDI構成に特化した静的コード分析ツールです。

コンテナーがその構成を100%検証することは実際には不可能ですが、検証することは依然として私にとって価値のある方法です。 単純に.Verify()を呼び出す.Verify()で、完全にバグが.Verify()か、動作するアプリケーションにさえなると考えるのはばかげているでしょう。 これがあなたのDI設定を「検証する」ことが意味するものであると誰かが思うかもしれませんが、私は彼らがこの機能が無価値であると主張する理由を理解します。 真実の声明のように聞こえます。 そのような機能を持っているふりをするSimple Injectorを含む、そこにコンテナはありません。

適用されたデコレータの順序が正しいかどうか、またはISomeService<T>すべての実装が実際にコンテナに登録されているかどうかなどを検出するための統合テストや単体テストの作成は、もちろん責任です。

コンテナの検証に対するMarkのブログからの2つの具体的な議論を述べたい。

コンテナーが検証する状況に入るのは簡単ですが、実行時にはまだ中断します。

私はそれに同意します、しかし私はSimple Injectorドキュメンテーションがhereこれにどうアプローチするかについていくつかの素晴らしい指導を得たと思いhere

設定上の規約をするとき、とにかくコンテナの中にあるべきではない登録を取得するのは簡単です。

とにかくこのような状況に陥らないようにするのは正しいことだと思うので、私はこの問題に遭遇したことは一度もない。

質問に戻る:

Simple Injectorのドキュメントのヒントの1つは抽象ファクトリを使うことですが、この場合はそうはしません。 すでに存在するもののためにファクトリーを作成することは私にとってかなり奇妙に思えます。 たぶんそれは正しい命名の問題にすぎませんが、なぜAccountControllerにAuthenticationFactoryまたはAuthenticationContextが必要なのでしょうか。 言い換えれば、ASP.NETアイデンティティーの設計上の問題により、アプリケーションで配線に関する問題が発生していることについて、アプリケーションで何か知っておく必要があるのはなぜでしょうか。

代わりに、 IAuthenticationManagerの登録を調整することによって、起動時/検証時に新しく作成されたOwinContextから認証コンポーネントを返し、実行時に「通常の」または設定されたAuthenticationManagerを返すことができます。 これは工場の必要性を取り除き、責任をそれがあるべき場所である構成ルートに移動します。 それでも、 .Verify()呼び出すことができながら、 IAuthenticationManagerを必要な場所に挿入できます。

コードは次のようになります。

container.RegisterPerWebRequest<IAuthenticationManager>(() => 
    AdvancedExtensions.IsVerifying(container) 
        ? new OwinContext(new Dictionary<string, object>()).Authentication 
        : HttpContext.Current.GetOwinContext().Authentication); 

ただし、このSOLIDソリューションではIAuthenticationManagerにまったく依存しないようにすることをおIAuthenticationManagerします。このインターフェースに依存すると、インターフェース分離原則に違反するため、プロキシ実装を作成して作成を遅らせることが難しくなります。

あなたはあなたのニーズとあなたのニーズだけに合う抽象を定義することによってこれをすることができました。 アイデンティティテンプレートがIAuthenticationManager呼び出すのを見ると、この抽象化は.SignOut()メソッドと.SignOut()メソッド以外には必要ありません。 しかし、これは、あなたがVisual Studioテンプレートによって「無料で」入手したAccountControllerAccountControllerを完全にリファクタリングすることをあなたに強いるでしょう。





simple-injector