接著上篇 asp.net mvc源碼分析-Action篇 DefaultModelBinder 我們已經擷取的了Action的參數,有前面的內容我們知道Action的調用時在ControllerActionInvoker類的InvokeActionMethod方法。
protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
object returnValue = actionDescriptor.Execute(controllerContext, parameters);
ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);
return result;
}
我們現在知道actionDescriptor是ReflectedActionDescriptor類的一個執行個體,
ReflectedActionDescriptor的Execute方法的實現大致如下
public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters) {
ParameterInfo[] parameterInfos = MethodInfo.GetParameters();
var rawParameterValues = from parameterInfo in parameterInfos
select ExtractParameterFromDictionary(parameterInfo, parameters, MethodInfo);
object[] parametersArray = rawParameterValues.ToArray();
ActionMethodDispatcher dispatcher = DispatcherCache.GetDispatcher(MethodInfo);
object actionReturnValue = dispatcher.Execute(controllerContext.Controller, parametersArray);
return actionReturnValue;
}
ParameterInfo[] parameterInfos = MethodInfo.GetParameters();這句沒什麼說的就是擷取Action的參數集合,大家應該知道方法參數中的parameters是什麼東西吧,一個以Action參數名為key,其值為value的一個字典結合。
var rawParameterValues = from parameterInfo in parameterInfos
select ExtractParameterFromDictionary(parameterInfo, parameters, MethodInfo);
object[] parametersArray = rawParameterValues.ToArray();
這兩句其實就是把parameters中參數value 按照Action中參數順序組成一個數組。ExtractParameterFromDictionary方法就是檢查parameters中的資料有效性。主要檢查parameters是否包含parameterInfo.Name,沒有拋異常,有則檢查是否為null,為null是就檢查 該參數是否允許為null,不允許則拋異常,不為null則檢查值是否是參數類型的一個執行個體。
ActionMethodDispatcherCache實現如下:
internal sealed class ActionMethodDispatcherCache : ReaderWriterCache<MethodInfo,ActionMethodDispatcher> {
public ActionMethodDispatcherCache() {}
public ActionMethodDispatcher GetDispatcher(MethodInfo methodInfo) {
return FetchOrCreateItem(methodInfo, () => new ActionMethodDispatcher(methodInfo));
}
}
這裡 的FetchOrCreateItem我們就不說,在ActionMethodDispatcherCache類是曾經說過。這裡的GetDispatcher其實是返回的ActionMethodDispatcher類的一個執行個體。
public ActionMethodDispatcher(MethodInfo methodInfo) {
_executor = GetExecutor(methodInfo);
MethodInfo = methodInfo;
}
其中 GetExecutor代碼如下:
private static ActionExecutor GetExecutor(MethodInfo methodInfo) { // Parameters to executor ParameterExpression controllerParameter = Expression.Parameter(typeof(ControllerBase), "controller"); ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); // Build parameter list List<Expression> parameters = new List<Expression>(); ParameterInfo[] paramInfos = methodInfo.GetParameters(); for (int i = 0; i < paramInfos.Length; i++) { ParameterInfo paramInfo = paramInfos[i]; BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); // valueCast is "(Ti) parameters[i]" parameters.Add(valueCast); } // Call method UnaryExpression instanceCast = (!methodInfo.IsStatic) ? Expression.Convert(controllerParameter, methodInfo.ReflectedType) : null; MethodCallExpression methodCall = methodCall = Expression.Call(instanceCast, methodInfo, parameters); // methodCall is "((TController) controller) method((T0) parameters[0], (T1) parameters[1], ...)" // Create function if (methodCall.Type == typeof(void)) { Expression<VoidActionExecutor> lambda = Expression.Lambda<VoidActionExecutor>(methodCall, controllerParameter, parametersParameter); VoidActionExecutor voidExecutor = lambda.Compile(); return WrapVoidAction(voidExecutor); } else { // must coerce methodCall to match ActionExecutor signature UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object)); Expression<ActionExecutor> lambda = Expression.Lambda<ActionExecutor>(castMethodCall, controllerParameter, parametersParameter); return lambda.Compile(); } }
其實這段代碼是很好理解的,就是用運算式樹狀架構來產生一個Action方法的調用。這段代碼最後返回的是一個ActionExecutor的委託,在這個GetExecutor方法中有一句很耗時的是
lambda.Compile(),大家想過這裡為什麼要用運算式樹狀架構而不直接調用MethodInfo的Invoke方法嗎?,調用Invoke方其實也很慢,最主要是調用過程是沒法緩衝的;而用運算式雖然編譯成委託時要慢點,但是這裡有一個ActionMethodDispatcherCache來保證每個Action調用所需的委託執行個體只需編譯一次,多次調用同一Action運算式就比Invoke方法效能高多了。看見微軟在mvc3中緩衝做的已經很好了。
object actionReturnValue = dispatcher.Execute(controllerContext.Controller, parametersArray);這句就是調用_executor委託,也就是真正執行Action方法,actionReturnValue 就是Action的傳回值,預設是一個ActionResult。
現在 再來看看 ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);這個方法吧:
protected virtual ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue) {
if (actionReturnValue == null) {
return new EmptyResult();
}
ActionResult actionResult = (actionReturnValue as ActionResult) ??
new ContentResult { Content = Convert.ToString(actionReturnValue, CultureInfo.InvariantCulture) };
return actionResult;
}
ActionResult actionResult = (actionReturnValue as ActionResult) ??new ContentResult { Content = Convert.ToString(actionReturnValue, CultureInfo.InvariantCulture) };就是 檢查Action返回的是否是一個ActionResult,正常境況下都是的。如果返回的不是則構造一個ContentResult 返回,我在想如果Action返回的不是ActionResult,我們throw是不是更好了。
如 我們的Action如下
public class HomeController : Controller
{
public object Index(){return new { Name="majiang"};}
}
返回結果: