243 lines
11 KiB
C#
243 lines
11 KiB
C#
|
namespace System.Web.UI.WebControls {
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Collections.Specialized;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Globalization;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
using System.Text;
|
|||
|
using System.Text.RegularExpressions;
|
|||
|
using System.Web;
|
|||
|
using System.Web.Resources;
|
|||
|
using System.Web.UI;
|
|||
|
using System.Web.UI.WebControls;
|
|||
|
|
|||
|
internal static class QueryableDataSourceHelper {
|
|||
|
// This regular expression verifies that parameter names are set to valid identifiers. This validation
|
|||
|
// needs to match the parser's identifier validation as done in the default block of NextToken().
|
|||
|
private static readonly string IdentifierPattern =
|
|||
|
@"^\s*[\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}_]" + // first character
|
|||
|
@"[\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\p{Nd}\p{Pc}\p{Mn}\p{Mc}\p{Cf}_]*"; // remaining characters
|
|||
|
|
|||
|
private static readonly Regex IdentifierRegex = new Regex(IdentifierPattern + @"\s*$");
|
|||
|
|
|||
|
private static readonly Regex AutoGenerateOrderByRegex = new Regex(IdentifierPattern +
|
|||
|
@"(\s+(asc|ascending|desc|descending))?\s*$", RegexOptions.IgnoreCase); // order operators
|
|||
|
|
|||
|
internal static IQueryable AsQueryable(object o) {
|
|||
|
IQueryable oQueryable = o as IQueryable;
|
|||
|
if (oQueryable != null) {
|
|||
|
return oQueryable;
|
|||
|
}
|
|||
|
|
|||
|
// Wrap strings in IEnumerable<string> instead of treating as IEnumerable<char>.
|
|||
|
string oString = o as string;
|
|||
|
if (oString != null) {
|
|||
|
return Queryable.AsQueryable(new string[] { oString });
|
|||
|
}
|
|||
|
|
|||
|
IEnumerable oEnumerable = o as IEnumerable;
|
|||
|
if (oEnumerable != null) {
|
|||
|
// IEnumerable<T> can be directly converted to an IQueryable<T>.
|
|||
|
Type genericType = FindGenericEnumerableType(o.GetType());
|
|||
|
if (genericType != null) {
|
|||
|
// The non-generic Queryable.AsQueryable gets called for array types, executing
|
|||
|
// the FindGenericType logic again. Might want to investigate way to avoid this.
|
|||
|
return Queryable.AsQueryable(oEnumerable);
|
|||
|
}
|
|||
|
// Wrap non-generic IEnumerables in IEnumerable<object>.
|
|||
|
List<object> genericList = new List<object>();
|
|||
|
foreach (object item in oEnumerable) {
|
|||
|
genericList.Add(item);
|
|||
|
}
|
|||
|
return Queryable.AsQueryable(genericList);
|
|||
|
}
|
|||
|
|
|||
|
// Wrap non-IEnumerable types in IEnumerable<T>.
|
|||
|
Type listType = typeof(List<>).MakeGenericType(o.GetType());
|
|||
|
IList list = (IList)DataSourceHelper.CreateObjectInstance(listType);
|
|||
|
list.Add(o);
|
|||
|
return Queryable.AsQueryable(list);
|
|||
|
}
|
|||
|
|
|||
|
public static IList ToList(this IQueryable query, Type dataObjectType) {
|
|||
|
MethodInfo toListMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(dataObjectType);
|
|||
|
return (IList)toListMethod.Invoke(null, new object[] { query });
|
|||
|
}
|
|||
|
|
|||
|
public static bool EnumerableContentEquals(IEnumerable enumerableA, IEnumerable enumerableB) {
|
|||
|
IEnumerator enumeratorA = enumerableA.GetEnumerator();
|
|||
|
IEnumerator enumeratorB = enumerableB.GetEnumerator();
|
|||
|
while (enumeratorA.MoveNext()) {
|
|||
|
if (!enumeratorB.MoveNext())
|
|||
|
return false;
|
|||
|
object itemA = enumeratorA.Current;
|
|||
|
object itemB = enumeratorB.Current;
|
|||
|
if (itemA == null) {
|
|||
|
if (itemB != null)
|
|||
|
return false;
|
|||
|
}
|
|||
|
else if (!itemA.Equals(itemB))
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (enumeratorB.MoveNext())
|
|||
|
return false;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public static Type FindGenericEnumerableType(Type type) {
|
|||
|
// Logic taken from Queryable.AsQueryable which accounts for Array types which are not
|
|||
|
// generic but implement the generic IEnumerable interface.
|
|||
|
while ((type != null) && (type != typeof(object)) && (type != typeof(string))) {
|
|||
|
if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(IEnumerable<>))) {
|
|||
|
return type;
|
|||
|
}
|
|||
|
foreach (Type interfaceType in type.GetInterfaces()) {
|
|||
|
Type genericInterface = FindGenericEnumerableType(interfaceType);
|
|||
|
if (genericInterface != null) {
|
|||
|
return genericInterface;
|
|||
|
}
|
|||
|
}
|
|||
|
type = type.BaseType;
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
internal static IDictionary<string, object> ToEscapedParameterKeys(this ParameterCollection parameters, HttpContext context, Control control) {
|
|||
|
if (parameters != null) {
|
|||
|
return parameters.GetValues(context, control).ToEscapedParameterKeys(control);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
internal static IDictionary<string, object> ToEscapedParameterKeys(this IDictionary parameters, Control owner) {
|
|||
|
Dictionary<string, object> escapedParameters = new Dictionary<string, object>(parameters.Count,
|
|||
|
StringComparer.OrdinalIgnoreCase);
|
|||
|
|
|||
|
foreach (DictionaryEntry de in parameters) {
|
|||
|
string key = (string)de.Key;
|
|||
|
if (String.IsNullOrEmpty(key)) {
|
|||
|
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
|
|||
|
AtlasWeb.LinqDataSourceView_ParametersMustBeNamed, owner.ID));
|
|||
|
|
|||
|
}
|
|||
|
ValidateParameterName(key, owner);
|
|||
|
escapedParameters.Add('@' + key, de.Value);
|
|||
|
}
|
|||
|
return escapedParameters;
|
|||
|
}
|
|||
|
|
|||
|
internal static IDictionary<string, object> ToEscapedParameterKeys(this IDictionary<string, object> parameters, Control owner) {
|
|||
|
Dictionary<string, object> escapedParameters = new Dictionary<string, object>(parameters.Count,
|
|||
|
StringComparer.OrdinalIgnoreCase);
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> parameter in parameters) {
|
|||
|
string key = parameter.Key;
|
|||
|
if (String.IsNullOrEmpty(key)) {
|
|||
|
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
|
|||
|
AtlasWeb.LinqDataSourceView_ParametersMustBeNamed, owner.ID));
|
|||
|
|
|||
|
}
|
|||
|
ValidateParameterName(key, owner);
|
|||
|
escapedParameters.Add('@' + key, parameter.Value);
|
|||
|
}
|
|||
|
return escapedParameters;
|
|||
|
}
|
|||
|
|
|||
|
internal static IQueryable CreateOrderByExpression(IOrderedDictionary parameters, IQueryable source, IDynamicQueryable queryable) {
|
|||
|
if (parameters != null && parameters.Count > 0) {
|
|||
|
//extract parameter values
|
|||
|
//extract the order by expression and apply it to the queryable
|
|||
|
string orderByExpression = GetOrderByClause(parameters.ToDictionary());
|
|||
|
if (!String.IsNullOrEmpty(orderByExpression)) {
|
|||
|
return queryable.OrderBy(source, orderByExpression);
|
|||
|
}
|
|||
|
}
|
|||
|
return source;
|
|||
|
}
|
|||
|
|
|||
|
internal static IQueryable CreateWhereExpression(IDictionary<string, object> parameters, IQueryable source, IDynamicQueryable queryable) {
|
|||
|
if (parameters != null && parameters.Count > 0) {
|
|||
|
//extract the where clause
|
|||
|
WhereClause clause = GetWhereClause(parameters);
|
|||
|
if (!String.IsNullOrEmpty(clause.Expression)) {
|
|||
|
//transform the current query with the where clause
|
|||
|
return queryable.Where(source, clause.Expression, clause.Parameters);
|
|||
|
}
|
|||
|
}
|
|||
|
return source;
|
|||
|
}
|
|||
|
|
|||
|
private static WhereClause GetWhereClause(IDictionary<string, object> whereParameters) {
|
|||
|
Debug.Assert((whereParameters != null) && (whereParameters.Count > 0));
|
|||
|
|
|||
|
WhereClause whereClause = new WhereClause();
|
|||
|
|
|||
|
whereClause.Parameters = new Dictionary<string, object>(whereParameters.Count);
|
|||
|
StringBuilder where = new StringBuilder();
|
|||
|
int index = 0;
|
|||
|
foreach (KeyValuePair<string, object> parameter in whereParameters) {
|
|||
|
string key = parameter.Key;
|
|||
|
string value = (parameter.Value == null) ? null : parameter.Value.ToString();
|
|||
|
// exclude null and empty values.
|
|||
|
if (!(String.IsNullOrEmpty(key) || String.IsNullOrEmpty(value))) {
|
|||
|
string newKey = "@p" + index++;
|
|||
|
if (where.Length > 0) {
|
|||
|
where.Append(" AND ");
|
|||
|
}
|
|||
|
where.Append(key);
|
|||
|
where.Append(" == ");
|
|||
|
where.Append(newKey);
|
|||
|
whereClause.Parameters.Add(newKey, parameter.Value);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
whereClause.Expression = where.ToString();
|
|||
|
return whereClause;
|
|||
|
}
|
|||
|
|
|||
|
private static string GetOrderByClause(IDictionary<string, object> orderByParameters) {
|
|||
|
Debug.Assert((orderByParameters != null) && (orderByParameters.Count > 0));
|
|||
|
|
|||
|
StringBuilder orderBy = new StringBuilder();
|
|||
|
foreach (KeyValuePair<string, object> parameter in orderByParameters) {
|
|||
|
string value = (string)parameter.Value;
|
|||
|
// exclude null and empty values.
|
|||
|
if (!String.IsNullOrEmpty(value)) {
|
|||
|
string name = parameter.Key;
|
|||
|
//validate parameter name
|
|||
|
ValidateOrderByParameter(name, value);
|
|||
|
|
|||
|
if (orderBy.Length > 0) {
|
|||
|
orderBy.Append(", ");
|
|||
|
}
|
|||
|
orderBy.Append(value);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return orderBy.ToString();
|
|||
|
}
|
|||
|
|
|||
|
internal static void ValidateOrderByParameter(string name, string value) {
|
|||
|
if (!AutoGenerateOrderByRegex.IsMatch(value)) {
|
|||
|
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
|
|||
|
AtlasWeb.LinqDataSourceView_InvalidOrderByFieldName, value, name));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal static void ValidateParameterName(string name, Control owner) {
|
|||
|
if (!IdentifierRegex.IsMatch(name)) {
|
|||
|
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
|
|||
|
AtlasWeb.LinqDataSourceView_InvalidParameterName, name, owner.ID));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private class WhereClause {
|
|||
|
public string Expression { get; set; }
|
|||
|
public IDictionary<string, object> Parameters { get; set; }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|