fork download
  1. //imaginarydevelopment.blogspot.com
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Linq.Expressions;
  7. using System.Reflection;
  8. using VisitorDelegate = System.Func<System.Linq.Expressions.Expression, System.Linq.Expressions.Expression>;
  9. using System.Diagnostics;
  10. using Adapter.Diagnostics;
  11. namespace Adapter
  12. {
  13. ///
  14. ///
  15. /// <remarks>http://b...content-available-to-author-only...n.com/b/alexj/archive/2010/03/01/tip-55-how-to-extend-an-iqueryable-by-wrapping-it.aspx</remarks>
  16. internal class InterceptingProvider : IQueryProvider
  17. {
  18. readonly IQueryProvider _underlyingProvider;
  19. readonly VisitorDelegate[] _visitors;
  20. readonly VisitorDelegate _afterUnderlyingVisitor;
  21.  
  22. private InterceptingProvider(VisitorDelegate afterUnderlyingVisitor,
  23. IQueryProvider underlyingQueryProvider,
  24. params VisitorDelegate[] visitors)
  25. {
  26. this._underlyingProvider = underlyingQueryProvider;
  27. this._afterUnderlyingVisitor = afterUnderlyingVisitor;
  28. this._visitors = visitors;
  29. }
  30.  
  31.  
  32. public static IQueryable<T> Intercept<T>(
  33. ExpressionVisitor afterUnderlyingVisitor,
  34. IQueryable<T> underlyingQuery,
  35. params ExpressionVisitor[] visitors)
  36. {
  37. Func<Expression, Expression>[] visitFuncs =
  38. visitors
  39. .Select(v => (Func<Expression, Expression>)v.Visit)
  40. .ToArray();
  41. VisitorDelegate afterDelegate = afterUnderlyingVisitor != null ? (VisitorDelegate)afterUnderlyingVisitor.Visit : null;
  42. return Intercept<T>(afterDelegate, underlyingQuery, visitFuncs);
  43. }
  44. public static IQueryable<T> Intercept<T>(
  45. IQueryable<T> underlyingQuery,
  46. params ExpressionVisitor[] visitors)
  47. {
  48. Func<Expression, Expression>[] visitFuncs =
  49. visitors
  50. .Select(v => (Func<Expression, Expression>)v.Visit)
  51. .ToArray();
  52. return Intercept<T>(null, underlyingQuery, visitFuncs);
  53. }
  54.  
  55. public static IQueryable<T> Intercept<T>(Func<Expression, Expression> afterUnderlyingVisitor,
  56. IQueryable<T> underlyingQuery,
  57. params Func<Expression, Expression>[] visitors)
  58. {
  59. var provider = new InterceptingProvider(afterUnderlyingVisitor,
  60. underlyingQuery.Provider,
  61. visitors
  62. );
  63. return provider.CreateQuery<T>(
  64. underlyingQuery.Expression);
  65. }
  66.  
  67. public static bool DoTrace = false;
  68. public IEnumerator<TElement> ExecuteQuery<TElement>(
  69. Expression expression)
  70. {
  71. Expression intercepted;
  72. using(var step=Profiler.Step("intercepting query")){
  73. intercepted = InterceptExpr(expression);
  74. }
  75. IQueryable newExpression;
  76. using(var step=Profiler.Step("Ef Translating query")){
  77. newExpression = _underlyingProvider.CreateQuery(intercepted);
  78. }
  79.  
  80. System.Diagnostics.Debug.Assert(intercepted.Type.FullName.Contains("Shared") == false);
  81. #if TRACE
  82. if(DoTrace)
  83. using(var step=Profiler.Step("ToTraceString")){
  84. Trace.WriteLine(((System.Data.Objects.ObjectQuery)newExpression).ToTraceString());
  85. }
  86. #endif
  87. if (_afterUnderlyingVisitor != null)
  88. {
  89. var afterResult = _afterUnderlyingVisitor(newExpression.Expression);
  90. }
  91. using(var step=Profiler.Step("enumerating query"))
  92. {
  93. var enumerator = newExpression.GetEnumerator();
  94. var enumeratorType = enumerator.GetType();
  95.  
  96. //get the type the enumerator contains
  97. var sourceArgumentType = enumeratorType.GetGenericArguments().Single();
  98.  
  99. var targetType = typeof(TElement);
  100. if (typeof(IEnumerator<TElement>).IsAssignableFrom(enumeratorType))
  101. return (IEnumerator<TElement>)enumerator;
  102.  
  103.  
  104. if (targetType.IsAssignableFrom(sourceArgumentType))
  105. {
  106. var items = new List<TElement>();
  107. while (enumerator.MoveNext())
  108. {
  109. var current = enumerator.Current;
  110.  
  111. items.Add((TElement)current);
  112. }
  113. return items.GetEnumerator();
  114. }
  115. //needs to translate one anonymous type to another
  116.  
  117. var targetConstructor = targetType.GetConstructor(targetType.GetGenericArguments());
  118. #warning does not handle recursive anonymous types
  119. var eProperties = from tp in targetType.GetProperties()
  120. join ep in sourceArgumentType.GetProperties()
  121. on tp.Name equals ep.Name
  122. select ep.GetGetMethod();
  123.  
  124.  
  125.  
  126. var items2 = new List<TElement>();
  127. while (enumerator.MoveNext())
  128. {
  129. var current = enumerator.Current;
  130. var targetParams = eProperties.Select(s => s.Invoke(current, null));
  131. var newItem = targetConstructor.Invoke(targetParams.ToArray());
  132.  
  133. items2.Add((TElement)newItem);
  134. }
  135.  
  136. return (IEnumerator<TElement>)items2.GetEnumerator();
  137. }
  138. }
  139.  
  140. TResult TranslateAnonymous<TResult>(Type inputType, object source)
  141. {
  142. var targetType = typeof(TResult);
  143. if (targetType.IsAssignableFrom(inputType))
  144. return (TResult)source;
  145. var targetConstructor = targetType.GetConstructor(targetType.GetGenericArguments());
  146. #warning does not handle nested anonymous types
  147. var eProperties = from tp in targetType.GetProperties()
  148. join ep in inputType.GetProperties()
  149. on tp.Name equals ep.Name
  150. select ep.GetGetMethod();
  151. var targetParams = eProperties.Select(s => s.Invoke(source, null));
  152. var result = targetConstructor.Invoke(targetParams.ToArray());
  153. return (TResult)result;
  154. }
  155.  
  156. public IQueryable<TElement> CreateQuery<TElement>(
  157. Expression expression)
  158. {
  159. return new InterceptedQuery<TElement>(this, expression);
  160. }
  161.  
  162. public IQueryable CreateQuery(Expression expression)
  163. {
  164. Type et = TypeHelper.FindIEnumerable(expression.Type);
  165. Type qt = typeof(InterceptedQuery<>).MakeGenericType(et);
  166. object[] args = new object[] { this, expression };
  167.  
  168. var ci = qt.GetConstructor(
  169. BindingFlags.NonPublic | BindingFlags.Instance,
  170. null,
  171. new Type[] {
  172. typeof(InterceptingProvider),
  173. typeof(Expression)
  174. },
  175. null);
  176.  
  177. return (IQueryable)ci.Invoke(args);
  178. }
  179.  
  180. public TResult Execute<TResult>(Expression expression)
  181. {
  182. var intercepted = InterceptExpr(expression);
  183. var result = this._underlyingProvider.Execute(intercepted);
  184. if (result == null)
  185. return default(TResult);
  186. return TranslateAnonymous<TResult>(result.GetType(), result);
  187. }
  188.  
  189. public object Execute(Expression expression)
  190. {
  191. return this._underlyingProvider.Execute(
  192. InterceptExpr(expression)
  193. );
  194. }
  195.  
  196. private Expression InterceptExpr(Expression expression)
  197. {
  198. Expression exp = expression;
  199. foreach (var visitor in _visitors)
  200. exp = visitor(exp);
  201. return exp;
  202. }
  203. }
  204.  
  205. }
  206.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty