1. 程式人生 > >Action 方法的執行(一)

Action 方法的執行(一)

數組 ttr 獲取 resolv static lte com nag binding

[toc]

  在 Aps.net mvc 應用中對請求的處理最終都是轉換為對某個 Controller 中的某個 Action 方法的調用,因此,要對一個請求進行處理,第一步,需要根據請求解析出對應的 ControllerAction 的名稱,這是 Asp.net mvc 中的路由 的職責所在,第二步,需要根據第一步解析出來的內容定位對請求進行處理的 Action 方法所屬的 Controller 類型,定位的過程稱為 Asp.net mvc 中 Controller 的激活,第三步,就是根據請求的的內容到第二步定位的 Controller 中查找最終用來處理請求的 Action 方法並執行。
  要執行一個方法,首先要根據方法簽名從多個重載(如果有)中選出最為匹配的一個,然後對方法的參數進行綁定,Action

方法的執行亦是如此。
  這一篇主要介紹一下 Action 方法執行過程的一些關鍵的組件。

關鍵組件

ActionInvoker

  在 Asp.net mvc 中的 Controller 類型是指那些直接或間接的實現了 IController 接口的類型,在該接口中僅定義了一個 void Execute(RequestContext requestContext) 方法,在通過 Controller 的激活過程成功創建 Controller 類型的實例後,後續的 Action 方法的執行便是由該方法來執行的,該方法是一個同步的方法,但默認情況下,創建 Controller 實例後的後續操作是異步實現的,這些異步的操作是通過 IAsyncController

接口來實現的,該接口繼承自 IController,定義如下:

    public interface IAsyncController : IController
    {
        IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);
        void EndExecute(IAsyncResult asyncResult);
    }
}

  Action 方法的執行是通過 IActionInvoker 來實現的,該接口中只定義了一個 bool InvokeAction(ControllerContext controllerContext, string actionName)

方法,該接口同樣具有一個異步的版本 IAsyncActionInvoker,該接口繼承自 IActionInvoker,其定義如下:

    public interface IAsyncActionInvoker : IActionInvoker
    {
        IAsyncResult BeginInvokeAction(ControllerContext controllerContext, string actionName, AsyncCallback callback, object state);
        bool EndInvokeAction(IAsyncResult asyncResult);
    }
}

  具體使用同步還是異步的版本來執行 Action,如果 Controller 執行的是同步的 Execute(RequestContext requestContext) 方法,則便使用同步的 IActionInvoker,否則使用的是異步的 IAsyncActionInvoker

ActionInvoker 的創建

  ActionInvoker 實例的創建是通過 Controller 類的 IActionInvoker CreateActionInvoker() 方法來創建的, IActionInvoker 的創建也是使用了 IOC 和工廠模式的思想,首先判斷內部 IOC 容器(IDependencyResolver 類型)是否存在 IAsyncActionInvokerFactory類型的綁定,如果存在則創建該類型的實例,然後調用其 CreateInstance() 創建 IActionInvoker 類型的實例並返回,否則,判斷 IOC 容器內是否存在 IActionInvokerFactory 類型的綁定,如果存在則創建該類型的實例,然後調用其 CreateInstance() 方法創建 IActionInvoker 類型的實例並返回。如果不存在,則判斷 IOC 容器內是否存在 IAsyncActionInvoker 類型的綁定,如果存在,則創建實例並返回,否則判斷是否存在 IActionInvoker 類型的綁定,如果存在,則創建實例並返回,如果不存在上述的任何類型的綁定,則創建一個 AsyncControllerActionInvoker 類型的實例並返回。
  

ControllerDescriptor

  該類從名字上就可以看出其是對 Controller 類型的一個描述,是一個抽象類,其定義(主要成員)如下:

    public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
    {
        //根據 ControllerContext 和 Action的名稱獲取指定的 Action的描述信息
        public abstract ActionDescriptor FindAction(ControllerContext controllerContext, string actionName)
        
        //獲取指定Controller 下定義的所有的有效的 Action的信息
        public abstract ActionDescriptor[] GetCanonicalActions();
        
        //ICustomAttributeProvider 接口方法,獲取指定Controller類型上使用的所有特性(Attribute) 的信息,參數 inherit 表示是否進行遞歸查找
        public virtual object[] GetCustomAttributes(bool inherit);
        //同屬 ICustomAttributeProvider 接口方法,返回指定類型標識的自定義屬性數組
        public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
        
        //獲取 Controller 類型上使用的過濾器屬性
        public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);
        
        //ICustomAttributeProvider接口方法,判斷指定類型的自定義屬性是否存在
        public virtual bool IsDefined(Type attributeType, bool inherit);
      
        //控制器的名稱
        public virtual string ControllerName;
        //控制器的類型
        public abstract Type ControllerType { get; }
        //IuniquelyIdentifiable接口成員
        public virtual string UniqueId;//當前類型的唯一標識,由控制器名稱、控制器類型以及當前類的類型生成
}

  上面的 GetCanonicalActions() 方法用來獲取有效的 Action 方法,那麽什麽是有效的 Action 方法?一個規範的 Action 方法應滿足如下的要求:

  • 所在類型必須直接或間接的繼承自抽象類 ControllerBase
  • 必須是一個公共的實例方法
  • 必須是一個可調用的方法(BindingFlags.InvokedMethod),例如,構造函數便不是有效的 Action 方法
  • 方法不具有泛型參數
  • 方法不具有 out 和 ref 參數

  ControllerDescriptor 是抽象類型,其具有兩個子類,ReflectedControllerDescriptorReflectedAsyncControllerDescriptor . 子類內部對父類中的方法進行重寫,從名稱上來看,一個是同步的,一個是異步的,要說清楚這兩個類最大的不同,需要說明一個類 ActionSelectorBase,這是一個抽象類,該類的主要成員如下示:

//屬性部分
public MethodInfo[] ActionMethods { get; private set; }//當前Controller下所有Action方法集合

public HashSet<MethodInfo> StandardRouteMethods { get; private set; }//所有 Action 方法的 HashSet集合

public MethodInfo[] AliasedMethods{get;}//所有具有別名的Action方法的集合
 
public ILookup<string, MethodInfo> NonAliasedMethods{get;private set;};//所有沒有別名的方法的集合

private StandardRouteActionMethodCache CreateStandardRouteCache()//Action 方法緩存,該類是該類內部的一個私有類,內部僅具有兩個屬性,分別表示無別名的 Action 方法的集合和具有別名方法的集合。

//方法部分
protected void Initialize(Type controllerType);//負責對上述的屬性進行初始化

protected abstract bool IsValidActionMethod(MethodInfo methodInfo);//判斷給定方法是否為有效的 Action方法,基本的判定標準即上面說的那些

//根據給定的 Action 名稱獲取對應的ActionMethod,在該方法中首先從緩存的具有別名的方法集合和無別名的方法集合中篩選中具有指定名稱的方法,然後改根據請求的方法(Post、Get等)對上一步得到的集合進行篩選,移除不滿足的項
protected List<MethodInfo> FindActionMethods(ControllerContext controllerContext, string actionName);

//獲取指定 Method 的 ActionName,如果方法沒有別名則直接返回它本身的名稱,如果方法有 ActionNameSelector 屬性修飾,則返回其Name屬性
public string GetActionName(MethodInfo methodInfo)

//根據指定的 ActionName 獲取對應的 ActionMethod,內部調用 FindActionMethods,如果不存在,返回 null,具有多個時拋出 AmbiguousMatchException 異常
public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName)

  該類具有兩個子類:ActionMethodSelectorAsyncActionMethodSelector,前者處理一些普通的同步方法相關的操作,後者處理一些異步方法相關的操作,這裏的異步方法包括兩種,一種是具有 Aysnc 前綴或具有 Completed 後綴的方法,此類方法主要是為了保持 Asp.net Mvc 的向後的兼容性,是 Asp.net mvc 3 之前實現異步的方式,另外一種是返回類型為 Task<ActionResult> 的方法。是 Asp.net mvc 3 之後提倡的異步 Action 實現方式。
  在 ControllerDescriptor 的兩個子類中均有一個 ActionMethodSelectorBase 類型的變量 _selector,不同的是 ReflectedControllerDescriptor 的該變量是 ActionMethodSelector 類型的,而 ReflectedAsyncControllerDescriptor 的該變量是 AsyncMethodSelector 類型的。
  在 ReflectedControllerDescriptorReflectedAsyncController 中均對基類中的 ActionDescriptor FindAction(ControllerContext context,string actionName) 進行重寫,前者內部調用 _selector 的同名方法返回一個與 actionName 匹配的 MethodInfo,最終返回的是上一步的 MethodInfoactionName 等作為參數初始化的一個 ReflectedActionDescriptor。對於後者,調用 _selector 的同名方法返回的是一個如下所示的委托

    delegate ActionDescriptor ActionDescriptorCreator(string actionName, ControllerDescriptor controllerDescriptor);}

  針對上述Action 方法的兩種不同的異步實現方法,返回的是不同的結果,對於具有 Async 前綴Completed 後綴的方法,返回的是 ReflectedAsyncActionDescriptor 類型的實例,對於返回類型為 Task<ActionResult> 的 Action 方法,返回的是 TaskAsyncActionDescriptor 類型的實例。

ActionDescriptor

  顧名思義,這個類用於描述 Action 相關的一些信息,該類是一個抽象類,其定義(主要成員)如下所示:

public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
{
 /*****************************屬性部分*********************************/
 //當前 Action 方法的名稱
 public abstract string ActionName { get; }
 
 //當前 Action 所在的 Controller相關的 ControllerDescriptor
 public abstract ControllerDescriptor ControllerDescriptor { get; }
 
 //Asp.net mvc 自定義的基於 Dictinary 的線程安全的緩存集合,後面會介紹
 internal ActionMethodDispatcherCache DispatcherCache{get{..};set{..}
 
  public virtual string UniqueId{};//IUniquelyIdentifiable 接口成員,當前類型實例的唯一標識
  由當前類型、ControllerDescriptor、ActionName三者計算產生

/***************************** 方法部分 ***********************************/

/ICustomAttributeProvider 接口方法,功能不在贅述,但這裏並沒有提供具體的實現,前兩個返回的均為空的 object[]
public virtual object[] GetCustomAttributes(bool inherit);

public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);

public virtual bool IsDefined(Type attributeType, bool inherit);

//其它方法
public abstract object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);//ActionDescriptor 類型的核心方法,Action 方法的返回的 ActionResult 便是由該方法的返回結果處理轉換產生的

//根據跟定的 ParameterInfo 去 parameters中尋找指定的參數值,key 為 parameterInfo.Name,查找失敗、參數類型不匹配、查找值為空但參數不能為空時拋出異常
internal static object ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, MethodInfo methodInfo);

//作用同上面的方法,但在出現上述拋出異常的情況時會使用默認值替代而不是拋出異常,這裏的默認值有兩種,一種是直接在方法參數後使用等號賦值的,如Say(string name = "tom"),另外一種是在 model 類的屬性上使用 DefaultValueAttribute 設置的默認值,前者具有更高的優先級
internal static object ExtractParameterOrDefaultFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters);

//獲取所有定義在當前 Action 上的過濾器Filters
public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);

//獲取所有的參數信息
public abstract ParameterDescriptor[] GetParameters();

  下面說一下 ActionMethodDispatcherCache 這個類型,該類型派生自 ReaderWriterCache,其中的ReaderWriterCache

ActionDescriptor 的子類

  ActionDescriptor 本身為抽象類,不能直接實例化使用,其類型具有兩個直接子類:ReflectedActionDescriptorAsyncActionDescriptor,後者亦是一個抽象類,其具有兩個子類型:ReflectedAsyncActionDescriptorTaskAsyncActionDescriptor。這裏主要對 ReflectedActionDescriptorTaskAsyncActionDescriptor 進行說明。

  • ReflectedActionDescriptor
      該類主要看其 object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters) 方法的實現,其實現如下所示:
public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }
            if (parameters == null)
            {
                throw new ArgumentNullException("parameters");
            }

            // Performance sensitive so avoid Linq or delegates.
            ParameterInfo[] parameterInfos = MethodInfo.GetParameters();
            object[] parametersArray = new object[parameterInfos.Length];
            for (int i = 0; i < parameterInfos.Length; i++)
            {
                ParameterInfo parameterInfo = parameterInfos[i];
                object parameter = ExtractParameterFromDictionary(parameterInfo, parameters, MethodInfo);
                parametersArray[i] = parameter;
            }

            ActionMethodDispatcher dispatcher = DispatcherCache.GetDispatcher(MethodInfo);
            object actionReturnValue = dispatcher.Execute(controllerContext.Controller, parametersArray);
            return actionReturnValue;
        }

  參數中的 ControllerContext 可以看作是 ControllerRouteDataRequestContext 三者的封裝,跟前面所說的一樣,根據傳入的 MethodInfo 實例獲取 ActionMethodDispatcher 類型實例,調用其 Execute 方法並 返回執行結果。

  • TaskAsyncActionDescriptor
      在說這個類型之前先看一下其直接基類:AsyncActionDescriptor,其定義如下:
public abstract class AsyncActionDescriptor : ActionDescriptor
    {
        public abstract IAsyncResult BeginExecute(ControllerContext controllerContext, IDictionary<string, object> parameters, AsyncCallback callback, object state);

        public abstract object EndExecute(IAsyncResult asyncResult);

        public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);//這裏直接拋出無效操作的異常

        internal static AsyncManager GetAsyncManager(ControllerBase controller)
        {
            IAsyncManagerContainer helperContainer = controller as IAsyncManagerContainer;
            if (helperContainer == null)
            {
                throw Error.AsyncCommon_ControllerMustImplementIAsyncManagerContainer(controller.GetType());
            }

            return helperContainer.AsyncManager;
        }
    }

  前兩個方法沒有什麽好說的,標準的異步操作方法,重點說一下最後一個方法,AsyncManager GetAysncManager(ControllerBase controller),該方法返回一個 AsyncManager類型,該類型主要功能有兩個,第一,在異步操作和回調操作之間傳遞參數,第二個是向系統發送異步操作開始和結束的通知。該類的定義如下:

//屬性部分
public event EventHandler Finished;//異步操作完成時的回調事件

public OperationCounter OutstandingOperations { get; private set; }//當前執行的異步操作的計數器

public IDictionary<string, object> Parameters { get; private set; }//異步操作與回調操作之間參數傳遞的集合

public int Timeout{get;set;}//異步操作的超時時間,單位為毫秒,默認值為 45*1000

//方法部分
public virtual void Finish();//異步操作完成時的回調函數

public virtual void Sync(Action action);異步操作的執行函數,內部調用 SynchronizationContext 實例的同名方法

  OutstandingOperations 屬性是整個異步操作的核心屬性,在異步操作開始和結束,應分別調用該類型的 Increment 方法和 Decrement 方法增加和減少計數操作(內部執行原子運算),每次在調用上述的兩個方法時,都會檢驗當前的計數值,如果計數值為0,則表示異步操作已完成,此時,該類型的 Completed 方法便會表調用,響應的 AsyncManager 類型的 Finish 方法也會被調用。抽象類 Controller 實現了 IAsyncManagerContainer 接口,該接口中僅定義了一個 AsyncManager 類型的屬性。因此,對一步操作超時進行設置的兩個屬性 :AsyncTimeoutAttributeNoAsyncTimeoutAttribute,其中前者繼承自 ActionFilter,重寫了過濾器的 void OnExecuting(ActionExecutingContext context) 方法,後者繼承自前者,相當於將超時時間 duration 設置為 -1,表示永不超時。在重寫的 void OnExecuting(ActionExecutingContext context) 便是對 Controller 的 AsyncManager 實例的 TimeOut 屬性進行設置。

ParameterDescriptor

  顧名思義,該類型是對 Action 方法的參數進行描述的,是一個抽象類,同樣的實現了 ICustomAttributeProvider 接口,該類的定義如下示:

public abstract class ParameterDescriptor : ICustomAttributeProvider
    {
        //參數所屬的 Action 的 ActionDescriptor
        public abstract ActionDescriptor ActionDescriptor { get; }

        //參數綁定信息,例如參數值的獲取路徑(Querstring、Form表單等)、參數是否需要進行綁定,是否具有某些前綴等信息
        public virtual ParameterBindingInfo BindingInfo{get;}

        //參數默認值
        public virtual object DefaultValue}{get;}
        
        //參數名稱
        public abstract string ParameterName { get; }

        //參數類型
        public abstract Type ParameterType { get; }

        //以下為 ICustomAttributeProvider 接口方法
        public virtual object[] GetCustomAttributes(bool inherit);
        
        public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
       
        public virtual bool IsDefined(Type attributeType, bool inherit);
    }

  該類型僅有一個實現類:ReflectedParameterDescriptor,在該類中對基類中的方法或屬性進行重寫或實現,增加了一個 ParameterInfo 類型的屬性,表示當前參數的一些信息。
  至此,Action 執行過程中一些關鍵的組件介紹完畢。

Action 方法的執行(一)