web-dev-qa-db-ja.com

属性の依存性注入

次のように依存関係をカスタムAuthorizeAttributeに挿入しようとしています。

public class UserCanAccessArea : AuthorizeAttribute
{
    readonly IPermissionService permissionService;

    public UserCanAccessArea() :
        this(DependencyResolver.Current.GetService<IPermissionService>()) { }

    public UserCanAccessArea(IPermissionService permissionService)
    {
        this.permissionService = permissionService;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

これは機能しますが、シングルトンとして解決しているようです。つまり、私の説明した問題が 前の質問

プロパティインジェクションを使用したいのですが、属性自体がUnityで解決されないため、プロパティをインターセプトして解決するようにコンテナーを構成する方法を見つけることができません。私は以下を試しました:

public class UserCanAccessArea : AuthorizeAttribute
{
    public IPermissionService permissionService { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

容器:

container.RegisterType<UserCanAccessArea>(new InjectionProperty("permissionService"));

ただし、このプロパティは実行時には常にnullです。

誰かがこれを達成しましたか?そうであればあなたは例を持っていますか?

21
James

属性への依存性注入を完全に行わないようにする必要があります。この理由については、この記事で説明しています 属性への依存性注入:しないでください! 。要約すると、この記事では次のことが説明されています。

  • Attributeインスタンスの作成はインターセプトできないため、コンストラクターインジェクションは不可能です。 CLRが制御しています。
  • プロパティインジェクションの使用は脆弱です。これは Temporal Coupling になるため、防止する必要があるためです。
  • 属性への依存性注入により、コンテナーの構成が正しいことを確認できなくなります。
  • MVCやWeb APIキャッシュ属性などのフレームワークにより、誤って キャプティブ依存関係 を作成し、バグを引き起こすことが非常に簡単になります。

ここでは2つの選択肢があります。

  1. Mark Seemannの 参考記事 および この関連記事 で説明されているように、データ(属性)をその動作(サービス)から分割して、属性をパッシブにします。
  2. この答え で説明されているように、属性を 謙虚なオブジェクト に変換します。これはあなたを意味します:
    1. すべての依存関係を含むカスタムサービスに、属性からすべてのロジックを抽出します。
    2. そのサービスをコンテナに登録します。
    3. 属性のメソッド(あなたの場合はAuthorizeCore)がサービスロケーター/ DependencyResolverからサービスを解決し、サービスのメソッドを呼び出す以外に何もさせません。ここで注意すべき重要な点は、コンストラクター注入、プロパティ注入を実行できず、サービスを属性プライベート状態に格納できないことです(既にお気づきのとおり)。

使用するオプション:

  • デザインをクリーンに保つことに非常に熱心である場合、またはこの方法で適用する必要がある属性がいくつかある場合、または属性を適用する場合は、System.Webに依存しないアセンブリで定義されているオプション1を使用します。 .Mvc。
  • それ以外の場合は、オプション2を使用します。
46
Steven