ASP.NET MVC 3 のアクションフィルタに依存性を注入する。この方法の何が問題なのでしょうか?
質問
以下はその設定です。あるサービスのインスタンスを必要とするアクションフィルタがあるとします。
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エクステンションには、バインディングを使ってアクションフィルタを設定する非常に強力な方法があります。詳しくは以下のリファレンスを参照してください。
- https://github.com/ninject/ninject.web.mvc/wiki/Dependency-injection-for-filters
- https://github.com/ninject/ninject.web.mvc/wiki/Conditional-bindings-for-filters
- https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations
アップデート 2
下のコメントで@usrさんが指摘されているように
ActionFilterAttribute
はクラスがロードされたときにインスタンス化され、アプリケーションのライフタイム全体にわたって存続します。もし
IMyService
インタフェースがシングルトンであることを想定していない場合、それは結局のところ
キャプティブディペンデンシー
. その実装がスレッドセーフでない場合、多くの苦痛に直面する可能性があります。
クラスの期待寿命よりも短い寿命を持つ依存関係があるときはいつでも、それを直接注入するのではなく、その依存関係をオンデマンドで生成するためにファクトリーを注入するのが賢明です。
関連
-
[解決済み】統合マネージドパイプラインモードで適用されないASP.NETの設定が検出された
-
[解決済み】バックスラッシュを含むパス文字列のエスケープシーケンスが認識されない件
-
[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール
-
[解決済み] [Solved] 不正な文字列値: '\xEFxBFxBD' for column
-
[解決済み】"指定されたパスのフォーマットはサポートされていません。"
-
[解決済み】「namespace」なのに「type」のように使われる。
-
[解決済み】名前 'ViewBag' が現在のコンテキストに存在しない - Visual Studio 2015
-
[解決済み] ELMAHをASP.NET MVCの[HandleError]属性で動作させる方法は?
-
[解決済み】部分ビューから特定のセクションにコンテンツを注入する ASP.NET MVC 3 with Razor View Engine
-
[解決済み】ASP.NET MVC 3 RazorでAjax.BeginFormを使用する。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】"出力タイプがクラスライブラリのプロジェクトは直接起動できない"
-
[解決済み】C#で四捨五入する方法
-
[解決済み] エンティティタイプ <type> は、現在のコンテキストのモデルの一部ではありません。
-
[解決済み】Sequence contains no matching element(シーケンスにマッチする要素がない
-
[解決済み】非静的メソッドはターゲットを必要とする
-
[解決済み】C# - パスに不正な文字がある場合
-
[解決済み】C#のequal to演算子でtextとvarcharのデータ型は互換性がない
-
[解決済み】Microsoft.Extensions.LoggingからILoggerを解決することができない
-
[解決済み】スレッド終了またはアプリケーションの要求により、I/O操作が中断されました。
-
[解決済み] シングルトン・パターンの欠点やデメリットは何ですか?[クローズド]