1 namespace System.Web.Mvc { 2 using System; 3 using System.Collections.Generic; 4 using System.ComponentModel; 5 using System.Diagnostics.CodeAnalysis; 6 using System.Globalization; 7 using System.Linq; 8 using System.Reflection; 9 using System.Web.Mvc.Resources; 10 11 public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable { 12 13 private readonly static ActionMethodDispatcherCache _staticDispatcherCache = new ActionMethodDispatcherCache(); 14 private ActionMethodDispatcherCache _instanceDispatcherCache; 15 private readonly Lazy<string> _uniqueId; 16 17 private static readonly ActionSelector[] _emptySelectors = new ActionSelector[0]; 18 ActionDescriptor()19 protected ActionDescriptor() { 20 _uniqueId = new Lazy<string>(CreateUniqueId); 21 } 22 23 public abstract string ActionName { 24 get; 25 } 26 27 public abstract ControllerDescriptor ControllerDescriptor { 28 get; 29 } 30 31 internal ActionMethodDispatcherCache DispatcherCache { 32 get { 33 if (_instanceDispatcherCache == null) { 34 _instanceDispatcherCache = _staticDispatcherCache; 35 } 36 return _instanceDispatcherCache; 37 } 38 set { 39 _instanceDispatcherCache = value; 40 } 41 } 42 43 [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces", Justification = "This is overridden elsewhere in System.Web.Mvc")] 44 public virtual string UniqueId { 45 get { 46 return _uniqueId.Value; 47 } 48 } 49 CreateUniqueId()50 private string CreateUniqueId() { 51 return DescriptorUtil.CreateUniqueId(GetType(), ControllerDescriptor, ActionName); 52 } 53 Execute(ControllerContext controllerContext, IDictionary<string, object> parameters)54 public abstract object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters); 55 ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, MethodInfo methodInfo)56 internal static object ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, MethodInfo methodInfo) { 57 object value; 58 59 if (!parameters.TryGetValue(parameterInfo.Name, out value)) { 60 // the key should always be present, even if the parameter value is null 61 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_ParameterNotInDictionary, 62 parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType); 63 throw new ArgumentException(message, "parameters"); 64 } 65 66 if (value == null && !TypeHelpers.TypeAllowsNullValue(parameterInfo.ParameterType)) { 67 // tried to pass a null value for a non-nullable parameter type 68 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_ParameterCannotBeNull, 69 parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType); 70 throw new ArgumentException(message, "parameters"); 71 } 72 73 if (value != null && !parameterInfo.ParameterType.IsInstanceOfType(value)) { 74 // value was supplied but is not of the proper type 75 string message = String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_ParameterValueHasWrongType, 76 parameterInfo.Name, methodInfo, methodInfo.DeclaringType, value.GetType(), parameterInfo.ParameterType); 77 throw new ArgumentException(message, "parameters"); 78 } 79 80 return value; 81 } 82 ExtractParameterOrDefaultFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters)83 internal static object ExtractParameterOrDefaultFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters) { 84 Type parameterType = parameterInfo.ParameterType; 85 86 object value; 87 parameters.TryGetValue(parameterInfo.Name, out value); 88 89 // if wrong type, replace with default instance 90 if (parameterType.IsInstanceOfType(value)) { 91 return value; 92 } 93 else { 94 object defaultValue; 95 if (ParameterInfoUtil.TryGetDefaultValue(parameterInfo, out defaultValue)) { 96 return defaultValue; 97 } 98 else { 99 return TypeHelpers.GetDefaultValue(parameterType); 100 } 101 } 102 } 103 GetCustomAttributes(bool inherit)104 public virtual object[] GetCustomAttributes(bool inherit) { 105 return GetCustomAttributes(typeof(object), inherit); 106 } 107 GetCustomAttributes(Type attributeType, bool inherit)108 public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { 109 if (attributeType == null) { 110 throw new ArgumentNullException("attributeType"); 111 } 112 113 return (object[])Array.CreateInstance(attributeType, 0); 114 } 115 GetFilterAttributes(bool useCache)116 internal virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache) { 117 return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>(); 118 } 119 120 [EditorBrowsable(EditorBrowsableState.Never)] 121 [Obsolete("Please call System.Web.Mvc.FilterProviders.Providers.GetFilters() now.", true)] GetFilters()122 public virtual FilterInfo GetFilters() { 123 return new FilterInfo(); 124 } 125 GetParameters()126 public abstract ParameterDescriptor[] GetParameters(); 127 128 [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may perform non-trivial work.")] GetSelectors()129 public virtual ICollection<ActionSelector> GetSelectors() { 130 return _emptySelectors; 131 } 132 IsDefined(Type attributeType, bool inherit)133 public virtual bool IsDefined(Type attributeType, bool inherit) { 134 if (attributeType == null) { 135 throw new ArgumentNullException("attributeType"); 136 } 137 138 return false; 139 } 140 VerifyActionMethodIsCallable(MethodInfo methodInfo)141 internal static string VerifyActionMethodIsCallable(MethodInfo methodInfo) { 142 // we can't call static methods 143 if (methodInfo.IsStatic) { 144 return String.Format(CultureInfo.CurrentCulture, 145 MvcResources.ReflectedActionDescriptor_CannotCallStaticMethod, 146 methodInfo, 147 methodInfo.ReflectedType.FullName); 148 } 149 150 // we can't call instance methods where the 'this' parameter is a type other than ControllerBase 151 if (!typeof(ControllerBase).IsAssignableFrom(methodInfo.ReflectedType)) { 152 return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType, 153 methodInfo, methodInfo.ReflectedType.FullName); 154 } 155 156 // we can't call methods with open generic type parameters 157 if (methodInfo.ContainsGenericParameters) { 158 return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallOpenGenericMethods, 159 methodInfo, methodInfo.ReflectedType.FullName); 160 } 161 162 // we can't call methods with ref/out parameters 163 ParameterInfo[] parameterInfos = methodInfo.GetParameters(); 164 foreach (ParameterInfo parameterInfo in parameterInfos) { 165 if (parameterInfo.IsOut || parameterInfo.ParameterType.IsByRef) { 166 return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters, 167 methodInfo, methodInfo.ReflectedType.FullName, parameterInfo); 168 } 169 } 170 171 // we can call this method 172 return null; 173 } 174 } 175 } 176