PageRenderTime 71ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/CompositeC1/Composite/Core/Extensions/IQueryableExtensionMethods.cs

#
C# | 302 lines | 132 code | 66 blank | 104 comment | 17 complexity | b2c46d87ebb6b86ada5a36d0ab0fd2ec MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * The contents of this web application are subject to the Mozilla Public License Version
  3. * 1.1 (the "License"); you may not use this web application except in compliance with
  4. * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
  5. *
  6. * Software distributed under the License is distributed on an "AS IS" basis,
  7. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. * for the specific language governing rights and limitations under the License.
  9. *
  10. * The Original Code is owned by and the Initial Developer of the Original Code is
  11. * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
  12. *
  13. * Section 11 of the License is EXPRESSLY amended to include a provision stating
  14. * that any dispute, including but not limited to disputes related to the enforcement
  15. * of the License, to which Composite A/S as owner of the Original Code, as Initial
  16. * Developer or in any other role, becomes a part to shall be governed by Danish law
  17. * and be initiated before the Copenhagen City Court ("K�benhavns Byret")
  18. */
  19. using System;
  20. using System.Linq;
  21. using System.Linq.Expressions;
  22. using System.Reflection;
  23. using Composite.Core.Types;
  24. using Composite.Data;
  25. using Composite.Data.Foundation;
  26. namespace Composite.Core.Extensions
  27. {
  28. /// <summary>
  29. /// </summary>
  30. /// <exclude />
  31. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
  32. public static class IQueryableExtensionMethods
  33. {
  34. private static readonly MethodInfo _miQueryableAny =
  35. (from methodInfo in typeof (Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
  36. where methodInfo.Name == "Any" && methodInfo.GetParameters().Length == 1
  37. select methodInfo).First();
  38. /// <exclude />
  39. public static IQueryable<T> Random<T>(this IQueryable<T> source) where T : class, IData
  40. {
  41. Verify.ArgumentNotNull(source, "source");
  42. return source.OrderBy(element => Guid.NewGuid());
  43. }
  44. /// <exclude />
  45. public static IQueryable<T> TakeRandom<T>(this IQueryable<T> source, int elementsToTake) where T : class, IData
  46. {
  47. return source.Random().Take(elementsToTake);
  48. }
  49. /// <exclude />
  50. public static bool IsEnumerableQuery<T>(this IQueryable<T> query)
  51. {
  52. if(query == null) return false;
  53. if(query is DataFacadeQueryable<T>)
  54. {
  55. return (query as DataFacadeQueryable<T>).IsEnumerableQuery;
  56. }
  57. return query.GetType().GetGenericTypeDefinition().FullName.StartsWith("System.Linq.EnumerableQuery");
  58. }
  59. /// <exclude />
  60. public static bool Any(this IQueryable queryable)
  61. {
  62. var type = queryable.GetType();
  63. if(type.IsGenericType)
  64. {
  65. var genericArguments = type.GetGenericArguments();
  66. if(genericArguments.Length == 1)
  67. {
  68. var queryType = typeof (IQueryable<>).MakeGenericType(genericArguments);
  69. if(queryType.IsAssignableFrom(type))
  70. {
  71. MethodInfo genericAnyMethod = _miQueryableAny.MakeGenericMethod(genericArguments);
  72. return (bool)genericAnyMethod.Invoke(null, new object[] { queryable });
  73. }
  74. }
  75. }
  76. foreach(object element in queryable)
  77. {
  78. return true;
  79. }
  80. return false;
  81. }
  82. /// <exclude />
  83. public static IOrderedQueryable OrderBy(this IQueryable source, Type dataType, string property)
  84. {
  85. return ApplyOrder(source, dataType, property, "OrderBy");
  86. }
  87. /// <exclude />
  88. public static IOrderedQueryable OrderBy(this IQueryable source, Type dataType, string property, bool descending)
  89. {
  90. string methodName = descending ? "OrderByDescending" : "OrderBy";
  91. return ApplyOrder(source, dataType, property, methodName);
  92. }
  93. /// <exclude />
  94. public static IOrderedQueryable OrderByDescending(this IQueryable source, Type dataType, string property)
  95. {
  96. return ApplyOrder(source, dataType, property, "OrderByDescending");
  97. }
  98. /// <exclude />
  99. public static IOrderedQueryable ThenBy(this IOrderedQueryable source, Type dataType, string property)
  100. {
  101. return ApplyOrder(source, dataType, property, "ThenBy");
  102. }
  103. /// <exclude />
  104. public static IOrderedQueryable ThenBy(this IOrderedQueryable source, Type dataType, string property, bool descending)
  105. {
  106. string methodName = descending ? "ThenByDescending" : "ThenBy";
  107. return ApplyOrder(source, dataType, property, methodName);
  108. }
  109. /// <exclude />
  110. public static IOrderedQueryable ThenByDescending(this IOrderedQueryable source, Type dataType, string property)
  111. {
  112. return ApplyOrder(source, dataType, property, "ThenByDescending");
  113. }
  114. /// <exclude />
  115. public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
  116. {
  117. return ApplyOrder<T>(source, property, "OrderBy");
  118. }
  119. /// <exclude />
  120. public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
  121. {
  122. return ApplyOrder<T>(source, property, "OrderByDescending");
  123. }
  124. /// <exclude />
  125. public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
  126. {
  127. return ApplyOrder<T>(source, property, "ThenBy");
  128. }
  129. /// <exclude />
  130. public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
  131. {
  132. return ApplyOrder<T>(source, property, "ThenByDescending");
  133. }
  134. /// <exclude />
  135. private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
  136. {
  137. return (IOrderedQueryable<T>)ApplyOrder(source, typeof(T), property, methodName);
  138. }
  139. private static IOrderedQueryable ApplyOrder(IQueryable source, Type type, string property, string methodName)
  140. {
  141. Verify.IsNotNull(property, "property cannot be null");
  142. Verify.IsNotNull(type, "type cannot be null");
  143. string[] props = property.Split('.');
  144. Type paramenetType = type;
  145. ParameterExpression arg = Expression.Parameter(paramenetType, "x");
  146. Expression expr = arg;
  147. foreach (string prop in props)
  148. {
  149. // use reflection (not ComponentModel) to mirror LINQ
  150. PropertyInfo pi = paramenetType.GetPropertiesRecursively(f=>f.Name == prop).FirstOrDefault();
  151. Verify.IsNotNull(pi, "Could not find property '{0}' on type '{1}'", property, type);
  152. expr = Expression.Property(expr, pi);
  153. paramenetType = pi.PropertyType;
  154. }
  155. Type delegateType = typeof(Func<,>).MakeGenericType(type, paramenetType);
  156. LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
  157. object result = typeof(Queryable).GetMethods().Single(
  158. method => method.Name == methodName
  159. && method.IsGenericMethodDefinition
  160. && method.GetGenericArguments().Length == 2
  161. && method.GetParameters().Length == 2)
  162. .MakeGenericMethod(type, paramenetType)
  163. .Invoke(null, new object[] { source, lambda });
  164. return (IOrderedQueryable)result;
  165. }
  166. #region Obsolete
  167. //public static IQueryable<T> TakeRandom<T>(this IQueryable<T> source, int elementsToTake) where T : class, IData
  168. //{
  169. // Verify.ArgumentCondition(elementsToTake > 0 && elementsToTake <= 25, "elementsToTake",
  170. // "Value should be between 1 and 25.");
  171. // int totalElementCount = source.Count();
  172. // if (totalElementCount == 0)
  173. // {
  174. // return new T[0].AsQueryable();
  175. // }
  176. // if (totalElementCount < elementsToTake)
  177. // {
  178. // elementsToTake = totalElementCount;
  179. // }
  180. // var subqueries = new IQueryable<T>[elementsToTake];
  181. // int position = 0;
  182. // foreach (int nextIndex in GetUniqueRandomSequence(0, totalElementCount - 1, elementsToTake))
  183. // {
  184. // subqueries[position++] = source.Skip(nextIndex).Take(1);
  185. // }
  186. // // Merging subqueries into a binary tree
  187. // int queryCount = elementsToTake;
  188. // while (queryCount > 1)
  189. // {
  190. // for (int i = 0; i < (queryCount + 1) / 2; i++)
  191. // {
  192. // subqueries[i] = (i * 2 < queryCount - 1)
  193. // ? subqueries[i * 2].Concat(subqueries[i * 2 + 1])
  194. // : subqueries[i * 2];
  195. // }
  196. // queryCount = (queryCount + 1) / 2;
  197. // }
  198. // return subqueries[0];
  199. //}
  200. //private static IEnumerable<int> GetUniqueRandomSequence(int minValue, int maxValue, int count)
  201. //{
  202. // int range = maxValue - minValue + 1;
  203. // if (count > range) throw new InvalidOperationException("'count' exceed number of possible unique random values");
  204. // var random = new Random();
  205. // if((double)count / range >= 0.3)
  206. // {
  207. // var values = new int[range];
  208. // for(int i=0; i<range; i++) values[i] = i;
  209. // int valuesLeft = range;
  210. // for (int i = 0; i < count; i++)
  211. // {
  212. // int index = random.Next(0, valuesLeft);
  213. // yield return minValue + values[index];
  214. // if (index < valuesLeft - 1)
  215. // {
  216. // values[index] = values[valuesLeft - 1];
  217. // }
  218. // valuesLeft--;
  219. // }
  220. // yield break;
  221. // }
  222. // // Another algoritm to get the values
  223. // List<int> usedNumbers = new List<int>(count);
  224. // while (usedNumbers.Count < count)
  225. // {
  226. // int randomInt = random.Next(minValue, maxValue);
  227. // if (usedNumbers.Contains(randomInt) == false)
  228. // {
  229. // usedNumbers.Add(randomInt);
  230. // yield return randomInt;
  231. // }
  232. // }
  233. //}
  234. #endregion Obsolete
  235. }
  236. }