1 namespace System.Web.Mvc {
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Diagnostics.CodeAnalysis;
6 using System.Globalization;
8 using System.Reflection;
9 using System.Web.Mvc.Resources;
11 public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable {
13 private readonly static ActionMethodDispatcherCache _staticDispatcherCache = new ActionMethodDispatcherCache();
14 private ActionMethodDispatcherCache _instanceDispatcherCache;
15 private readonly Lazy<string> _uniqueId;
17 private static readonly ActionSelector[] _emptySelectors = new ActionSelector[0];
19 protected ActionDescriptor() {
20 _uniqueId = new Lazy<string>(CreateUniqueId);
23 public abstract string ActionName {
27 public abstract ControllerDescriptor ControllerDescriptor {
31 internal ActionMethodDispatcherCache DispatcherCache {
33 if (_instanceDispatcherCache == null) {
34 _instanceDispatcherCache = _staticDispatcherCache;
36 return _instanceDispatcherCache;
39 _instanceDispatcherCache = value;
43 [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces", Justification = "This is overridden elsewhere in System.Web.Mvc")]
44 public virtual string UniqueId {
46 return _uniqueId.Value;
50 private string CreateUniqueId() {
51 return DescriptorUtil.CreateUniqueId(GetType(), ControllerDescriptor, ActionName);
54 public abstract object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);
56 internal static object ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, MethodInfo methodInfo) {
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");
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");
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");
83 internal static object ExtractParameterOrDefaultFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters) {
84 Type parameterType = parameterInfo.ParameterType;
87 parameters.TryGetValue(parameterInfo.Name, out value);
89 // if wrong type, replace with default instance
90 if (parameterType.IsInstanceOfType(value)) {
95 if (ParameterInfoUtil.TryGetDefaultValue(parameterInfo, out defaultValue)) {
99 return TypeHelpers.GetDefaultValue(parameterType);
104 public virtual object[] GetCustomAttributes(bool inherit) {
105 return GetCustomAttributes(typeof(object), inherit);
108 public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) {
109 if (attributeType == null) {
110 throw new ArgumentNullException("attributeType");
113 return (object[])Array.CreateInstance(attributeType, 0);
116 internal virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache) {
117 return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
120 [EditorBrowsable(EditorBrowsableState.Never)]
121 [Obsolete("Please call System.Web.Mvc.FilterProviders.Providers.GetFilters() now.", true)]
122 public virtual FilterInfo GetFilters() {
123 return new FilterInfo();
126 public abstract ParameterDescriptor[] GetParameters();
128 [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may perform non-trivial work.")]
129 public virtual ICollection<ActionSelector> GetSelectors() {
130 return _emptySelectors;
133 public virtual bool IsDefined(Type attributeType, bool inherit) {
134 if (attributeType == null) {
135 throw new ArgumentNullException("attributeType");
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,
147 methodInfo.ReflectedType.FullName);
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);
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);
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);
171 // we can call this method