ASP.NET MVCが関与している5つの同期と非同期、あなたは馬鹿かどうか分かりませんか?[前編]

17003 ワード

Actionメソッドの実行には、同期実行と非同期実行の2つの基本形式があるが、ASP.NETMVCの全体の体系の中で多くの同期/非同期の実行方式に関連して、前の相応の文章の中ですでにこれに対して相応の紹介をしたが、読者にこれに対して全体的な理解を持つために、私達は1つの総括的な論述をします.[本明細書は『How ASP.NET MVC Works?』に同期している]
ディレクトリ一、MvcHandlerの同期は非同期二、Controllerの同期と非同期三、ActionInvokerの同期と非同期四、ControllerDescriptorの同期と非同期五、ActionDescriptorの同期と非同期

一、MvcHandlerの同期と非同期


ASPについてNET MVCアプリケーションでは、MvcHandlerは最終的に要求を処理するためのHttpHandlerであり、UrlRoutingModuleというURLルーティングを実現したHttpModuleが対応する要求に動的にマッピングされる.MvcHandlerは、ControllerFactoryによってターゲットControllerをアクティブ化して実行し、実行終了後にアクティブ化されたControllerの解放を担当します.関連する内容は、本書の第3章「Controllerのアクティブ化」に参加してください.次のコード・スライスに示すように、MvcHandlerはIHttpHandlerとIHttpAsyncHandlerインタフェースを同時に実装するので、常にBeginProcessRequest/EndProcessRequestメソッドを呼び出して要求を非同期で処理します.
   1: public class MvcHandler : IHttpAsyncHandler, IHttpHandler, ...
   2: {
   3:     //       
   4:     IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
   5:     void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result);
   6:     void IHttpHandler.ProcessRequest(HttpContext httpContext);
   7: }

二、Controllerの同期と非同期


Controllerには、次のように定義された2つのインタフェースIControllerとIAsyncControllerをそれぞれ実現する同期と非同期の2つのバージョンもあります.アクティブなControllerオブジェクトがMvcHandlerのBeginProcessRequestメソッドで、ControllerのタイプがIAsyncControllerインタフェースを実装している場合、BeginExecute/EndExecuteメソッドを呼び出して非同期でControllerを実行する.そうでない場合、Controllerの実行はExecuteメソッドを呼び出すことによって同期的に実行されます.
   1: public interface IController
   2: {    
   3:     void Execute(RequestContext requestContext);
   4: }
   5: public interface IAsyncController : IController
   6: {    
   7:     IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);
   8:     void EndExecute(IAsyncResult asyncResult);
   9: }

デフォルトでは、Visual Studioウィザードで作成されるControllerタイプは、抽象タイプControllerのサブクラスです.次のコード・スライスに示すように、ControllerはIControllerとIAsyncControllerの2つのインタフェースを同時に実現しているので、MvcHandlerが要求処理を行う場合、常に非同期でControllerを実行する.
   1: public abstract class Controller : ControllerBase, IController, IAsyncController, ...
   2: {
   3:     //    
   4:     protected virtual bool DisableAsyncSupport
   5:     {
   6:         get{return false;}
   7:     }
   8: }

ただし、Controllerタイプには、非同期実行のサポートを無効にするかどうかを示す保護された読み取り専用プロパティDisableAsyncSupportがあります.デフォルトではこの属性値はFalseなので、デフォルトではControlの非同期実行がサポートされています.この属性を書き換えて値をTrueに設定すると、Controllerは同期でしか実行できません.具体的な実装ロジックは、DisableAsyncSupport属性がTrueの場合、BeginExecuteメソッドを呼び出すことによって(このメソッドは保護された虚メソッドExecuteCoreを呼び出し、最終的にControllerを同期して実行する).そうでない場合は、BeginExecuteCore/EndExecuteCoreを呼び出して非同期でControllerを実行します.
   1: public abstract class Controller: ...
   2: {
   3:     //    
   4:     protected virtual IAsyncResult BeginExecute(RequestContext requestContext, 
   5:     AsyncCallback callback, object state)
   6:     {
   7:         if (this.DisableAsyncSupport)
   8:         {
   9:             //    Execute      Controller
  10:         }
  11:         else
  12:         {
  13:             //    BeginExecuteCore/EndExecuteCore      Controller
  14:         }
  15: }
  16:     protected override void ExecuteCore();
  17:     protected virtual IAsyncResult BeginExecuteCore(AsyncCallback callback, object state);
  18:     protected virtual void EndExecuteCore(IAsyncResult asyncResult);
  19: }

三、ActionInvokerの同期と非同期


Modelバインドと検証を含むAction全体の実行は、ActionInvokerというコンポーネントによって行われますが、インタフェースIActionInvokerとIAsyncActionInvokerの2つのバージョンが同期と非同期の2つのバージョンで実現されています.次のコード・セグメントに示すように、これら2つのインタフェースは、それぞれ、InvokeActionおよびBeginInvokeAction/EndInvokeActionメソッドによって、同期および非同期の方法でActionを実行する.抽象クラスControllerには、ActionInvokerプロパティがあり、自分のActionを実行するためのActionInvokerオブジェクトを設定して返します.このオブジェクトは、最終的には保護された必要なメソッドCreateActionInvokerによって作成されます.
   1: public interface IActionInvoker
   2: {
   3:     bool InvokeAction(ControllerContext controllerContext, string actionName);
   4: }
   5:  
   6: public interface IAsyncActionInvoker : IActionInvoker
   7: {
   8:     IAsyncResult BeginInvokeAction(ControllerContext controllerContext, string actionName, AsyncCallback callback, object state);
   9:     bool EndInvokeAction(IAsyncResult asyncResult);
  10: }
  11:  
  12: public abstract class Controller
  13: {   
  14:     //    
  15:     public IActionInvoker ActionInvoker { get; set; }
  16:     protected virtual IActionInvoker CreateActionInvoker()
  17: }

ASP.NET MVCが実際にActionメソッドの同期および非同期実行に使用するActionInvokerは、それぞれControllerActionInvokerおよびAsyncControllerActionInvokerである.次のコード・セグメントに示すように、ControllerActionInvokerは、指定されたControllerコンテキストに従って対応するControllerDescriptorを取得するための保護されたメソッドGetControllerDescriptorを定義します.このメソッドは、サブクラスAsyncControllerActionInvokerによって書き換えられます.
   1: public class ControllerActionInvoker : IActionInvoker
   2: {
   3:     //    
   4:     protected virtual ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext);
   5: }
   6:  
   7: public class AsyncControllerActionInvoker : ControllerActionInvoker,IAsyncActionInvoker, IActionInvoker
   8: {
   9:     //    
  10:    protected override ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext);
  11: }

私たちが理解しなければならないのは、デフォルト(ControllerタイプのActionInvokerプロパティを明示的に設定していない)で使用されるActionInvokerタイプがどれなのかです.ASP.NET MVCがConrollerに採用するActionInvokerタイプの選択メカニズムは次のとおりです.
  • 現在のDependencyResolverを介してIAsyncActionInvokerインタフェースで登録されているActionInvokerを取得し、戻りオブジェクトがNullでない場合はデフォルトのActionInvokerとして使用します.
  • ・現在のDependencyResolverでIActionInvokerインタフェースで登録されているActionInvokerを取得し、戻りオブジェクトがNullでない場合はデフォルトのActionInvokerとする.
  • AsyncControllerActionInvokerオブジェクトをデフォルトのActionInvokerとして作成します.

  • デフォルトでは、現在のDependencyResolverは、指定したタイプを直接反射することで対応するインスタンスオブジェクトを提供します.したがって、前の2つのステップで返されるオブジェクトはNullなので、デフォルトで作成されるActionInvokerタイプはAsyncControllerActionInvokerです.これを以下の簡単な例で検証することができます.Visual Studioを通過するASP.NET MVCプロジェクトテンプレートで作成された空のWebアプリケーションでは、ActionメソッドIndexで直接ContentResultを介してActionInvokerプロパティのタイプ名を提示するデフォルトのHomeControllerを作成しました.
       1: public class HomeController : Controller
       2: {  
       3:     public ActionResult Index()
       4:     {
       5:         return Content("  ActionInvoker  :" + this.ActionInvoker.GetType().FullName);
       6:     }
       7: }

    このWebアプリケーションを実行すると、ブラウザ上で次のような出力結果が生成され、デフォルトで使用されているActionInvokerタイプがAsyncControllerActionInvokerであることが明らかになります.
       1:   ActionInvoker  :System.Web.Mvc.Async.AsyncControllerActionInvoker

    さらにDependencyResolverによるActionInvokerの提供メカニズムを検証するために、「ASP.NET MVC Controllerアクティブ化システム詳細:IoCの応用[下編]」で作成したNinjectベースのカスタムNinjectDependencyResolverをここに適用します.次のコード・スライスに示すように、NinjectDependencyResolverを初期化するときに、Control-ActionInvokerとAsyncControl-ActionInvokerの2つのカスタムActionInvokerタイプ、すなわちFooActionInvokerとFooAsyncActionInvokerにIActionInvokerとIAsyncActionInvokerを影射します.
       1: public class NinjectDependencyResolver : IDependencyResolver
       2: {
       3:     public IKernel Kernel { get; private set; }
       4:     public NinjectDependencyResolver()
       5:     {
       6:         this.Kernel = new StandardKernel();
       7:         AddBindings();
       8:     }
       9:     private void AddBindings()
      10:     {
      11:         this.Kernel.Bind<IActionInvoker>().To<FooActionInvoker>();
      12:         this.Kernel.Bind<IAsyncActionInvoker>().To<FooAsyncActionInvoker>();
      13:     }
      14:     public object GetService(Type serviceType)
      15:     {
      16:         return this.Kernel.TryGet(serviceType);
      17:     }
      18:     public IEnumerable<object> GetServices(Type serviceType)
      19:     {
      20:         return this.Kernel.GetAll(serviceType);
      21:     }
      22: }
      23: public class FooActionInvoker : ControllerActionInvoker
      24: {}
      25: public class FooAsyncActionInvoker : AsyncControllerActionInvoker
      26: {}

    GlobalでasaxでNinjectDependencyResolverを登録して私たちのプログラムを実行すると、ブラウザで次のような出力結果が得られます.IAsyncActionInvokerとFooAsyncActionInvokerが影を落とし、NinjectDependencyResolverはIAsyncActionInvokerを介してFooAsyncActionInvokerインスタンスを提供することができる.
       1:   ActionInvoker  :Artech.Mvc.FooAsyncActionInvoker

    NinjectDependencyResolverの定義を少し変更し、IAsyncActionInvokerインタフェースのタイプのシャドウを削除し、IActionInvokerのマッピングのみを保持します.
       1: public class NinjectDependencyResolver : IDependencyResolver
       2: {
       3:     //    
       4:     private void AddBindings()
       5:     {
       6:         this.Kernel.Bind<IActionInvoker>().To<FooActionInvoker>();
       7:         //this.Kernel.Bind<IAsyncActionInvoker>().To<FooAsyncActionInvoker>();
       8:     }
       9: }

    私たちのプログラムを再実行すると、次のような出力結果が得られます.NinjectDependencyResolverは、IActionInvokerインタフェースを介してのみ特定のActionInvokerを提供できるため、最終的に作成されるのはFooActionInvokerオブジェクトです.この例のプレゼンテーションでは、カスタムActionInvokerを使用する必要がある場合、DependencyResolverをカスタマイズしてIoCで特定のActionInvokerインスタンスを提供することができます.
       1:   ActionInvoker  :Artech.Mvc.FooActionInvoker

     
    ASP.NET MVCが関与している5つの同期と非同期、あなたは馬鹿かどうか分かりませんか?[前編]   ASP.NET MVCが関与している5つの同期と非同期、あなたは馬鹿かどうか分かりませんか?[下編]
    作成者:
    Artech
    出典:
    http://artech.cnblogs.com/
    本文の著作権は著者とブログ園に共有され、転載を歓迎するが、著者の同意を得ずにこの声明を保留し、文章のページの明らかな位置で原文の接続を与えなければならない.そうしないと、法律責任を追及する権利を保留する.
    分類:
    [01]技術剖析