1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
namespace System.Web.UI.WebControls.Expressions {
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Compilation;
using System.Web.DynamicData;
using System.Web.Resources;
using System.Web.UI.WebControls;
public class MethodExpression : ParameterDataSourceExpression {
private static readonly BindingFlags MethodFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
// We'll populate a list of ways to get the type
private Func<Type>[] typeGetters;
public string TypeName {
get {
return (string)ViewState["TypeName"] ?? String.Empty;
}
set {
ViewState["TypeName"] = value;
}
}
public string MethodName {
get {
return (string)ViewState["MethodName"] ?? String.Empty;
}
set {
ViewState["MethodName"] = value;
}
}
public bool IgnoreIfNotFound {
get {
object o = ViewState["IgnoreIfNotFound"];
return o != null ? (bool)o : false;
}
set {
ViewState["IgnoreIfNotFound"] = value;
}
}
public MethodExpression() {
// 1. If a TypeName is specified find the method on that type.
// 2. Otherwise, if the DataSource is an IDynamicDataSource, then use context type and search for the method.
// 3. Otherwise look for the method on the current TemplateControl (Page/UserControl) etc.
typeGetters = new Func<Type>[] {
() => GetType(TypeName),
() => GetType(DataSource),
() => (Owner != null && Owner.TemplateControl != null) ? Owner.TemplateControl.GetType() : null
};
}
private static Type GetType(string typeName) {
if (!String.IsNullOrEmpty(typeName)) {
return BuildManager.GetType(typeName, false /* throwOnError */, true /* ignoreCase */);
}
return null;
}
private static Type GetType(IQueryableDataSource dataSource) {
IDynamicDataSource dynamicDataSource = dataSource as IDynamicDataSource;
if (dynamicDataSource != null) {
return dynamicDataSource.ContextType;
}
return null;
}
internal MethodInfo ResolveMethod() {
if (String.IsNullOrEmpty(MethodName)) {
throw new InvalidOperationException(AtlasWeb.MethodExpression_MethodNameMustBeSpecified);
}
MethodInfo methodInfo = null;
// We allow the format string {0} in the method name
IDynamicDataSource dataSource = DataSource as IDynamicDataSource;
if (dataSource != null) {
MethodName = String.Format(CultureInfo.CurrentCulture, MethodName, dataSource.EntitySetName);
}
else if (MethodName.Contains("{0}")) {
// If method has a format string but no IDynamicDataSource then throw an exception
throw new InvalidOperationException(AtlasWeb.MethodExpression_DataSourceMustBeIDynamicDataSource);
}
foreach (Func<Type> typeGetter in typeGetters) {
Type type = typeGetter();
// If the type is null continue to next fall back function
if (type == null) {
continue;
}
methodInfo = type.GetMethod(MethodName, MethodFlags);
if (methodInfo != null) {
break;
}
}
return methodInfo;
}
public override IQueryable GetQueryable(IQueryable source) {
if (source == null) {
throw new ArgumentNullException("source");
}
MethodInfo method = ResolveMethod();
// Get the parameter values
IDictionary<string, object> parameterValues = GetValues();
if (method == null) {
if (IgnoreIfNotFound) {
// Unchange the IQueryable if the user set IgnoreIfNotFound
return source;
}
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
AtlasWeb.MethodExpression_MethodNotFound, MethodName));
}
if(!method.IsStatic) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
AtlasWeb.MethodExpression_MethodMustBeStatic, MethodName));
}
ParameterInfo[] parameterArray = method.GetParameters();
if (parameterArray.Length == 0 || !parameterArray[0].ParameterType.IsAssignableFrom(source.GetType())) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, AtlasWeb.MethodExpression_FirstParamterMustBeCorrectType,
MethodName, source.GetType()));
}
object[] arguments = new object[parameterArray.Length];
// First argument is the IQueryable
arguments[0] = source;
for (int i = 1; i < parameterArray.Length; ++i) {
ParameterInfo param = parameterArray[i];
object value;
if (!parameterValues.TryGetValue(param.Name, out value)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
AtlasWeb.MethodExpression_ParameterNotFound, MethodName, param.Name));
}
arguments[i] = DataSourceHelper.BuildObjectValue(value, param.ParameterType, param.Name);
}
object result = method.Invoke(null, arguments);
// Require the return type be the same as the parameter type
if (result != null) {
IQueryable queryable = result as IQueryable;
// Check if the user did a projection (changed the T in IQuerable<T>)
if (queryable == null || !queryable.ElementType.IsAssignableFrom(source.ElementType)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, AtlasWeb.MethodExpression_ChangingTheReturnTypeIsNotAllowed,
source.ElementType.FullName));
}
}
return (IQueryable)result;
}
}
}
|