閱讀145 返回首頁    go 阿裏雲 go 技術社區[雲棲]


ASP.NET MVC涉及到的5個同步與異步,你是否傻傻分不清楚?[上篇]

Action方法的執行具有兩種基本的形式,即同步執行和異步執行,而在ASP.NETMVC的整個體係中涉及到很多同步/異步的執行方式,雖然在前麵相應的文章中已經對此作了相應的介紹,為了讓讀者對此有一個整體的了解,我們來做一個總結性的論述。[本文已經同步到《How ASP.NET MVC Works?》中]

目錄
一、MvcHandler的同步於異步
二、Controller的同步與異步
三、ActionInvoker的同步與異步
四、ControllerDescriptor的同步與異步
五、ActionDescriptor的同步與異步

對於ASP.NET MVC應用來說,MvcHandler是最終用於處理請求的HttpHandler,它是通過UrlRoutingModule這個實現了URL路由的HttpModule被動態映射到相應的請求的。MvcHandler借助於ControllerFactory激活並執行目標Controller,並在執行結束後負責對激活的Controller進行釋放,相關的內容請參與本書的第3章“Controller的激活”。如下麵的代碼片斷所示,MvcHandler同時實現了IHttpHandler和IHttpAsyncHandler接口,所以它。

   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也具有同步與異步兩個版本,它們分別實現了具有如下定義的兩個接口IController和IAsyncController。當激活的Controller對象在MvcHandler的BeginProcessRequest方法中是按照這樣的方式執行的:。

   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這兩個接口,所以當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類型具有一個受保護的隻讀屬性用於表示是否禁用對異步執行的支持。在默認情況下,該屬性值為,所以默認情況下是支持Controller的異步執行的。如果我們通過重寫該屬性將值設置為True,那麼Controller將隻能以同步的方式執行。具體的實現邏輯體現在如下的代碼片斷中:BeginExecute方法在DisableAsyncSupport屬性為True的情況下通過調用Execute方法(該方法會調用一個受保護的虛方法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: }

包括Model綁定與驗證的整個Action的執行通過一個名為ActionInvoker的組件來完成,而它同樣具有同步和異步兩個版本,分別實現了接口IActionInvoker和IAsyncActionInvoker。如下麵的代碼片斷所示,這兩個接口分別通過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定義了一個受保護的方法GetControllerDescriptor用於根據指定的Controller上下文獲取相應的ControllerDescriptor,它的子類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直接通過對指定的類型進行反射來提供對應的實例對象,所以對於前麵兩個步驟返回的對象均為Null,所以默認創建出來的ActionInvoker類型為AsyncControllerActionInvoker。我們可以通過如下一個簡單的實例來驗證這一點。在通過Visual Studio的ASP.NET MVC項目模板創建的空Web應用中,我們創建了如下一個默認的HomeController,在Action方法Index中直接通過ContentResult將ActionInvoker屬性的類型名稱呈現出來。

   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的時候,我們將IActionInvoker和IAsyncActionInvoker影射到兩個自定義ActionInvoker類型,即FooActionInvoker和FooAsyncActionInvoker,它們分別繼承自ControllerActionInvoker和AsyncControllerActionInvoker。

   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個同步與異步,你是否傻傻分不清楚?[下篇]


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-26 11:04:36

  上一篇:go  ASP.NET MVC下的異步Action的定義和執行原理
  下一篇:go  ASP.NET MVC涉及到的5個同步與異步,你是否傻傻分不清楚?[下篇]