2012-01-06 10 views
6

私が書いているクラスライブラリで興味深いデザイン問題が発生しました。私は、クライアントがこのように使用することができるようにしたいAuthorizeAttributeのカスタム実装があります。クラスライブラリのAuthorizeAttributeへの依存関係の注入

[Protected("permission_name")] 

上記のコードでは、PermissionAttributeは(デフォルトコンテキストがのHttpContextを使用して作成)AuthorizeAttributeから継承し、ローカルのデフォルトを使用しています。

この属性では、SecurityServiceを使用してユーザー、ロール、およびアクセス許可をチェックします(SecurityService自体は、クライアント提供の永続性サービスを使用してアプリケーションの構成ルートに接続できます)。

したがって、私の属性には、機能するSecurityServiceへの参照が必要です。属性コンストラクタはコンパイル時定数しか持てないので、コンストラクタインジェクションは使用できません。

私はDIフレームワークを使用するように私のクライアントを強制的にしたくない - は、彼らがそう選択した場合、IoCのライブラリを使用せずにアップし、その組成のルートで必要な依存関係をとワイヤーを発見するために、彼らはできるはずです。ここで

は私のオプションは次のとおりです。

  • は、ライブラリの使用にシングルトンSecurityServiceを持っています。
  • 働くだろう使用プロパティ注射が、
    1. それが依存関係になるだろう、それは私がAUTHORIZE属性にMVCアプリのプロパティインジェクションを行うことができますどこ私にはわからないではないと
    2. れ、オプションのように見えます。

上記2に対する可能な解決策は次のように、アプリケーションの起動時に属性に静的プロパティとしてSecurityServiceのインスタンスを設定し、複数回に設定されることを防止するために、ガード句を使用しないことですこの:それは別の実装によって置き換え/拡張することができるように

class ProtectedAttribute : ... 
{ 
    private static ISecurityService _SecurityService ; 
    public static ISecurityService SecurityService 
    { 
     get 
     { 
      return _SecurityService ; 
     } 
     set 
     { 
      if (_SecurityService != null) 
       throw new InvalidOperationException("You can only set the SecurityService once per lifetime of this app.") ; 
      _SecurityService = value ; 
     } 
    } 
} 

SecurityServiceが抽象サービス・ファサードである可能性があります。

この問題を解決するには、より良い方法がありますか?

UPDATE:

public class ProtectedAttribute : ... 
{ 
    private string _Permission ; 
    public string Permission { get { return _Permission ; } /*...*/ } 

    public ProtectedAttribute(string permission) { /*...*/ } 
} 

セットアップの許可フィルタと依存関係を設定します。

は、アクセス権の名前を返す属性にパブリックプロパティを追加します。いくつかのコードを追加すると、私はそれをするつもりですか表示しますNinject(Ninjectを使用している場合)経由:

using Ninject.Web.Mvc.FilterBindingSyntax; 

public class MyModule : Ninject.Modules.NinjectModule 
{ 
    public override void Load() 
    { 
    // mySecurityService instance below can have a singleton lifetime - perfect! 
    this.BindFilter<MyAuthorizationFilter>(FilterScope.Action, 0) 
     .WhenActionMethodHas<ProtectedAttribute>() 
     .WithConstructorArgument("securityService", mySecurityService) 
     .WithConstructorArgumentFromActionAttribute<ProtectedAttribute>("permission", p => p.PermissionName) ; 
    } 
} 

あー、それはだ...美しい嗅ぐ

+0

このASP.NET MVC 3ですか? –

+0

@ダリン:はい、最低限のMVC 3が必要です。タグを更新しました。 –

+1

関連:http://stackoverflow.com/questions/7192543/injecting-dependencies-into-asp-net-mvc-3-action-filters-whats-wrong-with-this/7194467#7194467 –

答えて

7

ASP.NET MVC 3を使用すると、新しいIFilterProviderのおかげでアクションフィルタでコンストラクタインジェクションを使用できます。この方法では、コントローラーアクションをアクションフィルターで飾る必要がなくなりました。このインタフェースとマーカー属性を使用することで、それらを適用することができます。

手動で実装したくない場合は、アクションフィルターの依存関係を定義するためにfluent wayを提供するNinjectなどの既存のDIフレームワークを常に使用できます。

+0

+1の 'IFilterProviderそれは非常にきれいな解決策を提供します。同様の問題[ASP.NET MVCのIFilterProviderと懸念の分離](城ウィンザー)(http://stackoverflow.com/questions/10708565/asp-net-mvc-ifilterprovider-and-separation-of-concerns) –

0

私のアプリケーションは、IOCコンテナを公開する基本アプリケーションクラスから継承します。

public interface IInjectableApplication 
    { 
     IUnityContainer Container { get; } 
    } 

その後、私は属性が「正常」に構築されていないので、これは、真のインジェクションではありませんが、この

public abstract IocAwareActionFilterAttribute : ActionFilterAttribute{ 
    protected T ResolveItem<T>(ResultExecutedContext context) 
     { 
      var app = context.HttpContext.ApplicationInstance as IInjectableApplication; 
      if (app == null) { throw new NullReferenceException("Application is not IInjectable."); } 

      T c = (T)app.Container.Resolve(typeof(T)); 

      if (c == null) { throw new NullReferenceException(string.Format("Could not find injected {0}.", typeof(T).FullName)); } 
      return c; 
     } 

} 

を認識している基本属性クラスを、持っている、これは同様の動作を提供します。他のIOCに適応できない理由はありません