ActionDescriptor的作用是對Action方法的元數據的描述,通過ActionDescriptor我們可以獲取到action方法的相關的名稱,所屬控制器,方法的參數列表,應用到方法上的特性以及一些篩選器;ActionDescriptor是由ControllerDescriptor類中的FindAction方法進行創建;
ActionDescriptor類也繼承了ICustomAttributeProvider接口,所以ActionDescriptor類或是它的繼承類也實現了GetCustomAttributes和IsDefined方法;
ActionDescriptor類中的屬性和ControllerDescriptor類的屬性差不多,包含有一個含有描述操作符唯一性ID的 UniqueId,表示方法名稱的ActionName以及action所屬於的控制器的元數據描述類ControllerDescriptor等屬性字段;為了加快action方法的執行效率,ActionDescriptor類內部還創建了一個action方法調度的緩存屬性(ActionMethodDispatcherCache )DispatcherCache;
ActionMethodDispatcherCache 這個類結構是key為MethodInfo value 為ActionMethodDispatcher的字典緩存,在這個緩存類中通過GetDispatcher方法來快速獲取ActionMethodDispatcher類;
ReflectedActionDescriptor
ReflectedActionDescriptor類在MVC框架中繼承了ActionDescriptor類而且繼承了IMethodInfoActionDescriptor接口(獲取MethodInfo信息),並且覆蓋了一些父類的方法;
在ReflectedActionDescriptor類的構造函數中除了一些基本屬性的賦值以外,還會內部調用VerifyActionMethodIsCallable方法來對methodInfo屬性進行驗證,
VerifyActionMethodIsCallable方法的驗證邏輯:
1.方法不是靜態函數
2.方法的名稱不能是ControllerBase類中的方法
3.泛型方法中不能包含未賦值的泛型類型參數
4.方法的參數中不能有in 或是out修飾的參數
如果驗證不通過的話,直接throw一個ArgumentException異常;
在ReflectedActionDescriptor類中包含有一個GetFilterAttributes方法來獲取應用到action方法上的FilterAttribute的特性列表;
對於action方法中的參數的元數據的獲取是通過GetParameters方法,在ReflectedActionDescriptor類中有一個ParameterDescriptor[]的數組緩存,當緩存中存在時直接從緩存數組中獲取相應的參數元數據信息,如果沒有則通過MethodInfo的GetParameters方法獲取,然后調用ReflectedParameterDescriptor類的構造函數創建參數的元數據信息;
在Control可以存在同名的action方法,當時同名的action方法不能有相同的請求方式,我們可以標記一個action方式支持Post,Get等提交方式,在MVC框架中HttpGetAttribute,HttpPostAttribute等特性類都繼承了抽象類ActionMethodSelectorAttribute類,在ActionMethodSelectorAttribute類中只包含一個IsValidForRequest抽象方法
public abstract class ActionMethodSelectorAttribute : Attribute { public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo); }
在IsValidForRequest 方法目的是驗證當前action的方法是否與當前的請求類型相匹配;
在ReflectedActionDescriptor類中提供了獲取這些篩選特性的方法GetSelectors,這個方法內部會返回作用於當前action當前的ActionMethodSelectorAttribute類的子類的列表,由於這個返回值是ActionSelector類型的集合,而ActionSelector是一個參數為ControllerContext返回值為布爾類型的委托
public static ICollection<ActionSelector> GetSelectors(MethodInfo methodInfo) { ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), inherit: true); ActionSelector[] selectors = Array.ConvertAll(attrs, attr => (ActionSelector)(controllerContext => attr.IsValidForRequest(controllerContext, methodInfo))); return selectors; }
在ReflectedActionDescriptor類中還包含一個和GetSelectors方法類似的內部方法 GetNameSelectors,這個方法返回值為ActionNameSelector類型,這個類型也是一個委托類型,方法的作用是篩選ActionNameSelectorAttribute抽象類的子類的列表;其實內部實現和GetNameSelectors是相似的
public static ICollection<ActionNameSelector> GetNameSelectors(MethodInfo methodInfo) { ActionNameSelectorAttribute[] attrs = (ActionNameSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionNameSelectorAttribute), inherit: true); ActionNameSelector[] selectors = Array.ConvertAll(attrs, attr => (ActionNameSelector)((controllerContext, actionName) => attr.IsValidName(controllerContext, actionName, methodInfo))); return selectors; }
對於ActionNameSelectorAttribute與ActionMethodSelectorAttribute類的區別是前者是對action的名字進行篩選,而后者是對請求方式的篩選;
當獲得了ReflectedActionDescriptor類后就會執行action方法的執行,對於action方法的執行時直接調用類的Execute方法;
public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters)
在Execute方法的內部,由於考慮到性能,避免使用Linq或是委托;
首先通過MethodInfo.GetParameters獲取到參數信息列表,然后對參數列表進行遍歷驗證,參數驗證保證不能有重復的參數名,如果參數值為空的話要保證參數的類型是可以為空,當參數值不為空時,要保證參數值的類型和參數的類型一致;如果其中一條規則不符合時就會 throw ArgumentException 異常;
當參數列表遍歷完成后,就會在ActionMethodDispatcherCache緩存中通過GetDispatcher方法獲取到ActionMethodDispatcher類,然后調用ActionMethodDispatcher類來進行方法的調用,調用完成后執行結果返回;
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; }