1. ホーム
  2. c#

ASP.NET MVC 3 のアクションフィルタに依存性を注入する。この方法の何が問題なのでしょうか?

2023-09-11 02:12:51

質問

以下はその設定です。あるサービスのインスタンスを必要とするアクションフィルタがあるとします。

public interface IMyService
{
   void DoSomething();
}

public class MyService : IMyService
{
   public void DoSomething(){}
}

そして、そのサービスのインスタンスを必要とするActionFilterがあります。

public class MyActionFilter : ActionFilterAttribute
{
   private IMyService _myService; // <--- How do we get this injected

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
       _myService.DoSomething();
       base.OnActionExecuting(filterContext);
   }
}

MVC 1/2 において、アクションフィルタに依存性を注入することは、少し面倒でした。最も一般的なアプローチは、ここに見られるように、カスタムアクションの呼び出し元を使用することでした。 http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/ この回避策の背後にある主な動機は、この次のアプローチがずさんでコンテナと緊密に結合していると考えられたからです。

public class MyActionFilter : ActionFilterAttribute
{
   private IMyService _myService;

   public MyActionFilter()
      :this(MyStaticKernel.Get<IMyService>()) //using Ninject, but would apply to any container
   {

   }

   public MyActionFilter(IMyService myService)
   {
      _myService = myService;
   }

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
       _myService.DoSomething();
       base.OnActionExecuting(filterContext);
   }
}

ここではコンストラクタ注入を使用し、コンストラクタをオーバーロードしてコンテナを使用し、サービスを注入しています。コンテナとActionFilterを密に結合していることに同意します。

しかし、私の疑問はこれです。ASP.NET MVC 3 では、使用するコンテナを (DependencyResolver によって) 抽象化しますが、これらのすべてのフープはまだ必要でしょうか? 実演させてください。

public class MyActionFilter : ActionFilterAttribute
{
   private IMyService _myService;

   public MyActionFilter()
      :this(DependencyResolver.Current.GetService(typeof(IMyService)) as IMyService)
   {

   }

   public MyActionFilter(IMyService myService)
   {
      _myService = myService;
   }

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
       _myService.DoSomething();
       base.OnActionExecuting(filterContext);
   }
}

さて、一部の純粋主義者がこれを嘲笑うかもしれないことは知っていますが、真剣に、何が不都合なのでしょうか?テスト時にIMyServiceを受け取るコンストラクタを使用し、モックサービスを注入することができるため、テストが可能である。DependencyResolverを使っているので、DIコンテナの実装に縛られることもありませんし、このアプローチにデメリットはないでしょうか?

ちなみに、MVC3で新しいIFilterProviderインターフェイスを使用してこれを行うための別の素晴らしいアプローチを紹介します。 http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3

どのように解決するのですか?

属性 の部分) を使用し、次に実際に値を注入するコンストラクタを用意することができると思います。 フィルタ の部分)。

編集 : 少し読んでみたところ、これを行うための受け入れ可能な方法は、プロパティ注入によるもののようです。

public class MyActionFilter : ActionFilterAttribute
{
    [Injected]
    public IMyService MyService {get;set;}
    
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        MyService.DoSomething();
        base.OnActionExecuting(filterContext);
    }
}

については サービスロケータを使用しない理由 の質問について。それはほとんど依存性注入の柔軟性を減少させるだけです。たとえば、ロギングサービスをインジェクションしていて、ロギングサービスにインジェクション先のクラスの名前を自動的に与えたいとしたらどうでしょう?コンストラクタ注入を使用している場合、これは素晴らしい機能です。Dependency Resolver/Service Locator を使用している場合は、運が悪いことに、そのようなことはありません。

更新

これが答えとして受け入れられたので、私は記録しておきたいと思います。 Mark Seeman のアプローチ というのも、Action Filter の責任を Attribute から切り離すことができるからです。さらに、NinjectのMVC3エクステンションには、バインディングを使ってアクションフィルタを設定する非常に強力な方法があります。詳しくは以下のリファレンスを参照してください。

アップデート 2

下のコメントで@usrさんが指摘されているように ActionFilterAttribute はクラスがロードされたときにインスタンス化され、アプリケーションのライフタイム全体にわたって存続します。もし IMyService インタフェースがシングルトンであることを想定していない場合、それは結局のところ キャプティブディペンデンシー . その実装がスレッドセーフでない場合、多くの苦痛に直面する可能性があります。

クラスの期待寿命よりも短い寿命を持つ依存関係があるときはいつでも、それを直接注入するのではなく、その依存関係をオンデマンドで生成するためにファクトリーを注入するのが賢明です。