ASP.NET MVC Preview生命周期分析(三)2011-05-19 博客园 张善友详细看看InvokeAction方式的执行:protected internal bool InvokeAction(string actionName) { return InvokeAction (actionName, new RouteValueDictionary()); } protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values) { if (String.IsNullOrEmpty(actionName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName"); } // We have to loop through all the methods to make sure there isn"t // a conflict. If we stop the loop the first time we find a match // we might miss some error cases. MemberInfo[] membInfos = GetType().GetMember(actionName, MemberTypes.Method, BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public); MethodInfo foundMatch = null; foreach (MemberInfo memberInfo in membInfos) { MethodInfo mi = (MethodInfo)memberInfo; // 1) Action methods must not have the non-action attribute in their inheritance chain, and // 2) special methods like constructors, property accessors, and event accessors cannot be action methods, and // 3) methods originally defined on Object (like ToString) or Controller cannot be action methods. if (!mi.IsDefined(typeof(NonActionAttribute), true) && !mi.IsSpecialName && mi.DeclaringType.IsSubclassOf(typeof(Controller))) { if (foundMatch != null) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentUICulture, MvcResources.Controller_MoreThanOneAction, actionName, GetType())); } foundMatch = mi; } } if (foundMatch != null) { InvokeActionMethod(foundMatch, values); return true; } return false; }它通过反射获取所有同名 Action 方法信 息;其次,它过滤掉所有有 NonActionAttribute 和 IsSpecialName 标记的方法 ;第三,当同名有效 Action 被重载时它会抛出异常(提示 Controller_MoreThanOneAction),继续调用InvokeActionMethod:[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We use MethodInfo since it represents only methods and not constructors." + "This method only makes sense for use with methods.")] protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values) { if (methodInfo == null) { throw new ArgumentNullException("methodInfo"); } if (values == null) { values = new RouteValueDictionary(); } if (methodInfo.ContainsGenericParameters) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, MvcResources.Controller_ActionCannotBeGeneric, methodInfo.Name)); } ParameterInfo[] methodParameters = methodInfo.GetParameters(); object[] parameterValues = null; if (methodParameters.Length > 0) { parameterValues = new object[methodParameters.Length]; for (int i = 0; i < methodParameters.Length; i++) { ParameterInfo pi = methodParameters[i]; if (pi.IsOut || pi.ParameterType.IsByRef) { throw new InvalidOperationException(String.Format (CultureInfo.CurrentUICulture, MvcResources.Controller_ReferenceParametersNotSupported, pi.Name, methodInfo.Name)); } bool valueRequired = true; if (pi.ParameterType.IsClass) { // Classes (ref types) don"t require values since we can pass in null valueRequired = false; } else { if ((pi.ParameterType.IsGenericType && ! pi.ParameterType.IsGenericTypeDefinition) && (pi.ParameterType.GetGenericTypeDefinition() == typeof (Nullable<>))) { // Nullable types don"t require values since we can pass in null valueRequired = false; } } // Try to get a value for the parameter. We use this order of precedence: // 1. Explicitly-provided extra parameters in the call to InvokeAction() // 2. Values from the RouteData (could be from the typed-in URL or from the route"s default values) // 3. Request values (query string, form post data, cookie) object parameterValue = null; if (!values.TryGetValue (methodParameters[i].Name, out parameterValue)) { if (RouteData == null || !RouteData.Values.TryGetValue (methodParameters[i].Name, out parameterValue)) { if (Request != null) { parameterValue = Request[methodParameters[i].Name]; } } } if (parameterValue == null && valueRequired) { throw new InvalidOperationException(String.Format (CultureInfo.CurrentUICulture, MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name)); } try { parameterValues[i] = ConvertParameterType (parameterValue, methodParameters[i].ParameterType, methodParameters [i].Name, methodInfo.Name); } catch (Exception ex) { // Parameter value conversion errors are acceptable unless the value is required if (valueRequired) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name), ex); } } } } InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues)); }