0
|
1 using System;
|
|
2 using System.Collections;
|
|
3 using System.Collections.Generic;
|
|
4 using System.Data;
|
|
5 using System.Linq;
|
|
6 using System.Linq.Expressions;
|
|
7 using System.Reflection;
|
|
8
|
|
9 namespace BLToolkit.Data.Linq.Builder
|
|
10 {
|
|
11 using BLToolkit.Linq;
|
|
12 using Common;
|
|
13 using Data.Sql;
|
|
14 using Data.Sql.SqlProvider;
|
|
15 using Mapping;
|
|
16 using Reflection;
|
|
17
|
|
18 public partial class ExpressionBuilder
|
|
19 {
|
|
20 #region Sequence
|
|
21
|
|
22 static readonly object _sync = new object();
|
|
23
|
|
24 static List<ISequenceBuilder> _sequenceBuilders = new List<ISequenceBuilder>
|
|
25 {
|
|
26 new TableBuilder (),
|
|
27 new SelectBuilder (),
|
|
28 new SelectManyBuilder (),
|
|
29 new WhereBuilder (),
|
|
30 new OrderByBuilder (),
|
|
31 new GroupByBuilder (),
|
|
32 new JoinBuilder (),
|
|
33 new TakeSkipBuilder (),
|
|
34 new DefaultIfEmptyBuilder(),
|
|
35 new DistinctBuilder (),
|
|
36 new FirstSingleBuilder (),
|
|
37 new AggregationBuilder (),
|
|
38 new ScalarSelectBuilder (),
|
|
39 new CountBuilder (),
|
|
40 new PassThroughBuilder (),
|
|
41 new TableAttributeBuilder(),
|
|
42 new InsertBuilder (),
|
|
43 new InsertBuilder.Into (),
|
|
44 new InsertBuilder.Value (),
|
|
45 new InsertOrUpdateBuilder(),
|
|
46 new UpdateBuilder (),
|
|
47 new UpdateBuilder.Set (),
|
|
48 new DeleteBuilder (),
|
|
49 new ContainsBuilder (),
|
|
50 new AllAnyBuilder (),
|
|
51 new ConcatUnionBuilder (),
|
|
52 new IntersectBuilder (),
|
|
53 new CastBuilder (),
|
|
54 new OfTypeBuilder (),
|
|
55 new AsUpdatableBuilder (),
|
|
56 };
|
|
57
|
|
58 public static void AddBuilder(ISequenceBuilder builder)
|
|
59 {
|
|
60 _sequenceBuilders.Add(builder);
|
|
61 }
|
|
62
|
|
63 #endregion
|
|
64
|
|
65 #region Init
|
|
66
|
|
67 readonly Query _query;
|
|
68 readonly List<ISequenceBuilder> _builders = _sequenceBuilders;
|
|
69 private bool _reorder;
|
|
70 readonly Dictionary<Expression,Expression> _expressionAccessors;
|
|
71 private HashSet<Expression> _subQueryExpressions;
|
|
72
|
|
73 readonly public List<ParameterAccessor> CurrentSqlParameters = new List<ParameterAccessor>();
|
|
74
|
|
75 #if FW4 || SILVERLIGHT
|
|
76
|
|
77 readonly public List<ParameterExpression> BlockVariables = new List<ParameterExpression>();
|
|
78 readonly public List<Expression> BlockExpressions = new List<Expression>();
|
|
79 public bool IsBlockDisable;
|
|
80
|
|
81 #else
|
|
82 public bool IsBlockDisable = true;
|
|
83 #endif
|
|
84
|
|
85 readonly HashSet<Expression> _visitedExpressions;
|
|
86
|
|
87 public ExpressionBuilder(
|
|
88 Query query,
|
|
89 IDataContextInfo dataContext,
|
|
90 Expression expression,
|
|
91 ParameterExpression[] compiledParameters)
|
|
92 {
|
|
93 _query = query;
|
|
94 _expressionAccessors = expression.GetExpressionAccessors(ExpressionParam);
|
|
95
|
|
96 CompiledParameters = compiledParameters;
|
|
97 DataContextInfo = dataContext;
|
|
98 OriginalExpression = expression;
|
|
99
|
|
100 _visitedExpressions = new HashSet<Expression>();
|
|
101 Expression = ConvertExpressionTree(expression);
|
|
102 _visitedExpressions = null;
|
|
103 }
|
|
104
|
|
105 #endregion
|
|
106
|
|
107 #region Public Members
|
|
108
|
|
109 public readonly IDataContextInfo DataContextInfo;
|
|
110 public readonly Expression OriginalExpression;
|
|
111 public readonly Expression Expression;
|
|
112 public readonly ParameterExpression[] CompiledParameters;
|
|
113 public readonly List<IBuildContext> Contexts = new List<IBuildContext>();
|
|
114
|
|
115 private ISqlProvider _sqlProvider;
|
|
116 public ISqlProvider SqlProvider
|
|
117 {
|
|
118 get { return _sqlProvider ?? (_sqlProvider = DataContextInfo.CreateSqlProvider()); }
|
|
119 }
|
|
120
|
|
121 public static readonly ParameterExpression ContextParam = Expression.Parameter(typeof(QueryContext), "context");
|
|
122 public static readonly ParameterExpression DataContextParam = Expression.Parameter(typeof(IDataContext), "dctx");
|
|
123 public static readonly ParameterExpression DataReaderParam = Expression.Parameter(typeof(IDataReader), "rd");
|
|
124 public static readonly ParameterExpression ParametersParam = Expression.Parameter(typeof(object[]), "ps");
|
|
125 public static readonly ParameterExpression ExpressionParam = Expression.Parameter(typeof(Expression), "expr");
|
|
126
|
|
127 public MappingSchema MappingSchema
|
|
128 {
|
|
129 get { return DataContextInfo.MappingSchema; }
|
|
130 }
|
|
131
|
|
132 #endregion
|
|
133
|
|
134 #region Builder SQL
|
|
135
|
|
136 internal Query<T> Build<T>()
|
|
137 {
|
|
138 var sequence = BuildSequence(new BuildInfo((IBuildContext)null, Expression, new SqlQuery()));
|
|
139
|
|
140 if (_reorder)
|
|
141 lock (_sync)
|
|
142 {
|
|
143 _reorder = false;
|
|
144 _sequenceBuilders = _sequenceBuilders.OrderByDescending(_ => _.BuildCounter).ToList();
|
|
145 }
|
|
146
|
|
147 _query.Init(sequence, CurrentSqlParameters);
|
|
148
|
|
149 var param = Expression.Parameter(typeof(Query<T>), "info");
|
|
150
|
|
151 sequence.BuildQuery((Query<T>)_query, param);
|
|
152
|
|
153 return (Query<T>)_query;
|
|
154 }
|
|
155
|
|
156 [JetBrains.Annotations.NotNull]
|
|
157 public IBuildContext BuildSequence(BuildInfo buildInfo)
|
|
158 {
|
|
159 buildInfo.Expression = buildInfo.Expression.Unwrap();
|
|
160
|
|
161 var n = _builders[0].BuildCounter;
|
|
162
|
|
163 foreach (var builder in _builders)
|
|
164 {
|
|
165 if (builder.CanBuild(this, buildInfo))
|
|
166 {
|
|
167 var sequence = builder.BuildSequence(this, buildInfo);
|
|
168
|
|
169 lock (builder)
|
|
170 builder.BuildCounter++;
|
|
171
|
|
172 _reorder = _reorder || n < builder.BuildCounter;
|
|
173
|
|
174 return sequence;
|
|
175 }
|
|
176
|
|
177 n = builder.BuildCounter;
|
|
178 }
|
|
179
|
|
180 throw new LinqException("Sequence '{0}' cannot be converted to SQL.", buildInfo.Expression);
|
|
181 }
|
|
182
|
|
183 public SequenceConvertInfo ConvertSequence(BuildInfo buildInfo, ParameterExpression param)
|
|
184 {
|
|
185 buildInfo.Expression = buildInfo.Expression.Unwrap();
|
|
186
|
|
187 foreach (var builder in _builders)
|
|
188 if (builder.CanBuild(this, buildInfo))
|
|
189 return builder.Convert(this, buildInfo, param);
|
|
190
|
|
191 throw new LinqException("Sequence '{0}' cannot be converted to SQL.", buildInfo.Expression);
|
|
192 }
|
|
193
|
|
194 public bool IsSequence(BuildInfo buildInfo)
|
|
195 {
|
|
196 buildInfo.Expression = buildInfo.Expression.Unwrap();
|
|
197
|
|
198 foreach (var builder in _builders)
|
|
199 if (builder.CanBuild(this, buildInfo))
|
|
200 return builder.IsSequence(this, buildInfo);
|
|
201
|
|
202 return false;
|
|
203 }
|
|
204
|
|
205 #endregion
|
|
206
|
|
207 #region ConvertExpression
|
|
208
|
|
209 public ParameterExpression SequenceParameter;
|
|
210
|
|
211 Expression ConvertExpressionTree(Expression expression)
|
|
212 {
|
|
213 var expr = ConvertParameters(expression);
|
|
214
|
|
215 expr = ExposeExpression (expr);
|
|
216 expr = OptimizeExpression(expr);
|
|
217
|
|
218 var paramType = expr.Type;
|
|
219 var isQueryable = false;
|
|
220
|
|
221 if (expression.NodeType == ExpressionType.Call)
|
|
222 {
|
|
223 var call = (MethodCallExpression)expression;
|
|
224
|
|
225 if (call.IsQueryable() && call.Object == null && call.Arguments.Count > 0 && call.Type.IsGenericType)
|
|
226 {
|
|
227 var type = call.Type.GetGenericTypeDefinition();
|
|
228
|
|
229 if (type == typeof(IQueryable<>) || type == typeof(IEnumerable<>))
|
|
230 {
|
|
231 var arg = call.Type.GetGenericArguments();
|
|
232
|
|
233 if (arg.Length == 1)
|
|
234 {
|
|
235 paramType = arg[0];
|
|
236 isQueryable = true;
|
|
237 }
|
|
238 }
|
|
239 }
|
|
240 }
|
|
241
|
|
242 SequenceParameter = Expression.Parameter(paramType, "cp");
|
|
243
|
|
244 var sequence = ConvertSequence(new BuildInfo((IBuildContext)null, expr, new SqlQuery()), SequenceParameter);
|
|
245
|
|
246 if (sequence != null)
|
|
247 {
|
|
248 if (sequence.Expression.Type != expr.Type)
|
|
249 {
|
|
250 if (isQueryable)
|
|
251 {
|
|
252 var p = sequence.ExpressionsToReplace.SingleOrDefault(s => s.Path.NodeType == ExpressionType.Parameter);
|
|
253
|
|
254 return Expression.Call(
|
|
255 ((MethodCallExpression)expr).Method.DeclaringType,
|
|
256 "Select",
|
|
257 new[] { p.Path.Type, paramType },
|
|
258 sequence.Expression,
|
|
259 Expression.Lambda(p.Expr, (ParameterExpression)p.Path));
|
|
260 }
|
|
261
|
|
262 throw new InvalidOperationException();
|
|
263 }
|
|
264
|
|
265 return sequence.Expression;
|
|
266 }
|
|
267
|
|
268 return expr;
|
|
269 }
|
|
270
|
|
271 #region ConvertParameters
|
|
272
|
|
273 Expression ConvertParameters(Expression expression)
|
|
274 {
|
|
275 return expression.Convert(expr =>
|
|
276 {
|
|
277 switch (expr.NodeType)
|
|
278 {
|
|
279 case ExpressionType.Parameter:
|
|
280 if (CompiledParameters != null)
|
|
281 {
|
|
282 var idx = Array.IndexOf(CompiledParameters, (ParameterExpression)expr);
|
|
283
|
|
284 if (idx > 0)
|
|
285 return
|
|
286 Expression.Convert(
|
|
287 Expression.ArrayIndex(
|
|
288 ParametersParam,
|
|
289 Expression.Constant(Array.IndexOf(CompiledParameters, (ParameterExpression)expr))),
|
|
290 expr.Type);
|
|
291 }
|
|
292
|
|
293 break;
|
|
294 }
|
|
295
|
|
296 return expr;
|
|
297 });
|
|
298 }
|
|
299
|
|
300 #endregion
|
|
301
|
|
302 #region ExposeExpression
|
|
303
|
|
304 Expression ExposeExpression(Expression expression)
|
|
305 {
|
|
306 return expression.Convert(expr =>
|
|
307 {
|
|
308 switch (expr.NodeType)
|
|
309 {
|
|
310 case ExpressionType.MemberAccess:
|
|
311 {
|
|
312 var me = (MemberExpression)expr;
|
|
313 var l = ConvertMethodExpression(me.Member);
|
|
314
|
|
315 if (l != null)
|
|
316 {
|
|
317 var body = l.Body.Unwrap();
|
|
318 var ex = body.Convert2(wpi => new ExpressionHelper.ConvertInfo(wpi.NodeType == ExpressionType.Parameter ? me.Expression : wpi));
|
|
319
|
|
320 if (ex.Type != expr.Type)
|
|
321 ex = new ChangeTypeExpression(ex, expr.Type);
|
|
322
|
|
323 return ExposeExpression(ex);
|
|
324 }
|
|
325
|
|
326 break;
|
|
327 }
|
|
328
|
|
329 case ExpressionType.Constant :
|
|
330 {
|
|
331 var c = (ConstantExpression)expr;
|
|
332
|
|
333 // Fix Mono behaviour.
|
|
334 //
|
|
335 //if (c.Value is IExpressionQuery)
|
|
336 // return ((IQueryable)c.Value).Expression;
|
|
337
|
|
338 if (c.Value is IQueryable && !(c.Value is ITable))
|
|
339 {
|
|
340 var e = ((IQueryable)c.Value).Expression;
|
|
341
|
|
342 if (!_visitedExpressions.Contains(e))
|
|
343 {
|
|
344 _visitedExpressions.Add(e);
|
|
345 return ExposeExpression(e);
|
|
346 }
|
|
347 }
|
|
348
|
|
349 break;
|
|
350 }
|
|
351 }
|
|
352
|
|
353 return expr;
|
|
354 });
|
|
355 }
|
|
356
|
|
357 #endregion
|
|
358
|
|
359 #region OptimizeExpression
|
|
360
|
|
361 private MethodInfo[] _enumerableMethods;
|
|
362 public MethodInfo[] EnumerableMethods
|
|
363 {
|
|
364 get { return _enumerableMethods ?? (_enumerableMethods = typeof(Enumerable).GetMethods()); }
|
|
365 }
|
|
366
|
|
367 private MethodInfo[] _queryableMethods;
|
|
368 public MethodInfo[] QueryableMethods
|
|
369 {
|
|
370 get { return _queryableMethods ?? (_queryableMethods = typeof(Queryable).GetMethods()); }
|
|
371 }
|
|
372
|
|
373 readonly Dictionary<Expression,Expression> _optimizedExpressions = new Dictionary<Expression,Expression>();
|
|
374
|
|
375 Expression OptimizeExpression(Expression expression)
|
|
376 {
|
|
377 Expression expr;
|
|
378
|
|
379 if (_optimizedExpressions.TryGetValue(expression, out expr))
|
|
380 return expr;
|
|
381
|
|
382 _optimizedExpressions[expression] = expr = expression.Convert(OptimizeExpressionImpl);
|
|
383
|
|
384 return expr;
|
|
385 }
|
|
386
|
|
387 Expression OptimizeExpressionImpl(Expression expr)
|
|
388 {
|
|
389 switch (expr.NodeType)
|
|
390 {
|
|
391 case ExpressionType.MemberAccess:
|
|
392 {
|
|
393 var me = (MemberExpression)expr;
|
|
394
|
|
395 // Replace Count with Count()
|
|
396 //
|
|
397 if (me.Member.Name == "Count")
|
|
398 {
|
|
399 var isList = typeof(ICollection).IsAssignableFrom(me.Member.DeclaringType);
|
|
400
|
|
401 if (!isList)
|
|
402 isList = me.Member.DeclaringType.GetInterfaces()
|
|
403 .Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IList<>));
|
|
404
|
|
405 if (isList)
|
|
406 {
|
|
407 var mi = EnumerableMethods
|
|
408 .First(m => m.Name == "Count" && m.GetParameters().Length == 1)
|
|
409 .MakeGenericMethod(TypeHelper.GetElementType(me.Expression.Type));
|
|
410
|
|
411 return Expression.Call(null, mi, me.Expression);
|
|
412 }
|
|
413 }
|
|
414
|
|
415 if (CompiledParameters == null && TypeHelper.IsSameOrParent(typeof(IQueryable), expr.Type))
|
|
416 {
|
|
417 var ex = ConvertIQueriable(expr);
|
|
418
|
|
419 if (ex != expr)
|
|
420 return ConvertExpressionTree(ex);
|
|
421 }
|
|
422
|
|
423 return ConvertSubquery(expr);
|
|
424 }
|
|
425
|
|
426 case ExpressionType.Call :
|
|
427 {
|
|
428 var call = (MethodCallExpression)expr;
|
|
429
|
|
430 if (call.IsQueryable())
|
|
431 {
|
|
432 switch (call.Method.Name)
|
|
433 {
|
|
434 case "Where" : return ConvertWhere (call);
|
|
435 case "GroupBy" : return ConvertGroupBy (call);
|
|
436 case "SelectMany" : return ConvertSelectMany(call);
|
|
437 case "Select" : return ConvertSelect (call);
|
|
438 case "LongCount" :
|
|
439 case "Count" :
|
|
440 case "Single" :
|
|
441 case "SingleOrDefault" :
|
|
442 case "First" :
|
|
443 case "FirstOrDefault" : return ConvertPredicate (call);
|
|
444 case "Min" :
|
|
445 case "Max" : return ConvertSelector (call, true);
|
|
446 case "Sum" :
|
|
447 case "Average" : return ConvertSelector (call, false);
|
|
448 case "ElementAt" :
|
|
449 case "ElementAtOrDefault" : return ConvertElementAt (call);
|
|
450 }
|
|
451 }
|
|
452 else
|
|
453 {
|
|
454 var l = ConvertMethodExpression(call.Method);
|
|
455
|
|
456 if (l != null)
|
|
457 return OptimizeExpression(ConvertMethod(call, l));
|
|
458
|
|
459 if (CompiledParameters == null && TypeHelper.IsSameOrParent(typeof(IQueryable), expr.Type))
|
|
460 {
|
|
461 var attr = GetTableFunctionAttribute(call.Method);
|
|
462
|
|
463 if (attr == null)
|
|
464 {
|
|
465 var ex = ConvertIQueriable(expr);
|
|
466
|
|
467 if (ex != expr)
|
|
468 return ConvertExpressionTree(ex);
|
|
469 }
|
|
470 }
|
|
471 }
|
|
472
|
|
473 return ConvertSubquery(expr);
|
|
474 }
|
|
475 }
|
|
476
|
|
477 return expr;
|
|
478 }
|
|
479
|
|
480 LambdaExpression ConvertMethodExpression(MemberInfo mi)
|
|
481 {
|
|
482 var attrs = mi.GetCustomAttributes(typeof(MethodExpressionAttribute), true);
|
|
483
|
|
484 if (attrs.Length == 0)
|
|
485 return null;
|
|
486
|
|
487 MethodExpressionAttribute attr = null;
|
|
488
|
|
489 foreach (MethodExpressionAttribute a in attrs)
|
|
490 {
|
|
491 if (a.SqlProvider == SqlProvider.Name)
|
|
492 {
|
|
493 attr = a;
|
|
494 break;
|
|
495 }
|
|
496
|
|
497 if (a.SqlProvider == null)
|
|
498 attr = a;
|
|
499 }
|
|
500
|
|
501 if (attr != null)
|
|
502 {
|
|
503 Expression expr;
|
|
504
|
|
505 if (mi is MethodInfo && ((MethodInfo)mi).IsGenericMethod)
|
|
506 {
|
|
507 var method = (MethodInfo)mi;
|
|
508 var args = method.GetGenericArguments();
|
|
509 var names = args.Select(t => t.Name).ToArray();
|
|
510 var name = string.Format(attr.MethodName, names);
|
|
511
|
|
512 if (name != attr.MethodName)
|
|
513 expr = Expression.Call(mi.DeclaringType, name, Array<Type>.Empty);
|
|
514 else
|
|
515 expr = Expression.Call(mi.DeclaringType, name, args);
|
|
516 }
|
|
517 else
|
|
518 {
|
|
519 expr = Expression.Call(mi.DeclaringType, attr.MethodName, Array<Type>.Empty);
|
|
520 }
|
|
521
|
|
522 var call = Expression.Lambda<Func<LambdaExpression>>(Expression.Convert(expr, typeof(LambdaExpression)));
|
|
523
|
|
524 return call.Compile()();
|
|
525 }
|
|
526
|
|
527 return null;
|
|
528 }
|
|
529
|
|
530 Expression ConvertSubquery(Expression expr)
|
|
531 {
|
|
532 var ex = expr;
|
|
533
|
|
534 while (ex != null)
|
|
535 {
|
|
536 switch (ex.NodeType)
|
|
537 {
|
|
538 default : return expr;
|
|
539 case ExpressionType.MemberAccess : ex = ((MemberExpression)ex).Expression; break;
|
|
540 case ExpressionType.Call :
|
|
541 {
|
|
542 var call = (MethodCallExpression)ex;
|
|
543
|
|
544 if (call.Object == null)
|
|
545 {
|
|
546 if (call.IsQueryable()) switch (call.Method.Name)
|
|
547 {
|
|
548 case "Single" :
|
|
549 case "SingleOrDefault" :
|
|
550 case "First" :
|
|
551 case "FirstOrDefault" :
|
|
552 return ConvertSingleOrFirst(expr, call);
|
|
553 }
|
|
554
|
|
555 return expr;
|
|
556 }
|
|
557
|
|
558 ex = call.Object;
|
|
559
|
|
560 break;
|
|
561 }
|
|
562 }
|
|
563 }
|
|
564
|
|
565 return expr;
|
|
566 }
|
|
567
|
|
568 Expression ConvertSingleOrFirst(Expression expr, MethodCallExpression call)
|
|
569 {
|
|
570 var param = Expression.Parameter(call.Type, "p");
|
|
571 var selector = expr.Convert(e => e == call ? param : e);
|
|
572 var method = GetQueriableMethodInfo(call, (m, _) => m.Name == call.Method.Name && m.GetParameters().Length == 1);
|
|
573 var select = call.Method.DeclaringType == typeof(Enumerable) ?
|
|
574 EnumerableMethods
|
|
575 .Where(m => m.Name == "Select" && m.GetParameters().Length == 2)
|
|
576 .First(m => m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2) :
|
|
577 QueryableMethods
|
|
578 .Where(m => m.Name == "Select" && m.GetParameters().Length == 2)
|
|
579 .First(m => m.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2);
|
|
580
|
|
581 call = (MethodCallExpression)OptimizeExpression(call);
|
|
582 select = select.MakeGenericMethod(call.Type, expr.Type);
|
|
583 method = method.MakeGenericMethod(expr.Type);
|
|
584
|
|
585 return Expression.Call(null, method,
|
|
586 Expression.Call(null, select,
|
|
587 call.Arguments[0],
|
|
588 Expression.Lambda(selector, param)));
|
|
589 }
|
|
590
|
|
591 #endregion
|
|
592
|
|
593 #region ConvertWhere
|
|
594
|
|
595 Expression ConvertWhere(MethodCallExpression method)
|
|
596 {
|
|
597 var sequence = OptimizeExpression(method.Arguments[0]);
|
|
598 var predicate = OptimizeExpression(method.Arguments[1]);
|
|
599 var lambda = (LambdaExpression)predicate.Unwrap();
|
|
600 var lparam = lambda.Parameters[0];
|
|
601 var lbody = lambda.Body;
|
|
602
|
|
603 if (lambda.Parameters.Count > 1)
|
|
604 return method;
|
|
605
|
|
606 var exprs = new List<Expression>();
|
|
607
|
|
608 lbody.Visit(ex =>
|
|
609 {
|
|
610 if (ex.NodeType == ExpressionType.Call)
|
|
611 {
|
|
612 var call = (MethodCallExpression)ex;
|
|
613
|
|
614 if (call.Arguments.Count > 0)
|
|
615 {
|
|
616 var arg = call.Arguments[0];
|
|
617
|
|
618 if (call.IsQueryable(AggregationBuilder.MethodNames))
|
|
619 {
|
|
620 while (arg.NodeType == ExpressionType.Call && ((MethodCallExpression) arg).Method.Name == "Select")
|
|
621 arg = ((MethodCallExpression) arg).Arguments[0];
|
|
622
|
|
623 if (arg.NodeType == ExpressionType.Call)
|
|
624 exprs.Add(ex);
|
|
625 }
|
|
626 else if (call.IsQueryable(CountBuilder.MethodNames))
|
|
627 {
|
|
628 //while (arg.NodeType == ExpressionType.Call && ((MethodCallExpression) arg).Method.Name == "Select")
|
|
629 // arg = ((MethodCallExpression) arg).Arguments[0];
|
|
630
|
|
631 if (arg.NodeType == ExpressionType.Call)
|
|
632 exprs.Add(ex);
|
|
633 }
|
|
634 }
|
|
635 }
|
|
636 });
|
|
637
|
|
638 Expression expr = null;
|
|
639
|
|
640 if (exprs.Count > 0)
|
|
641 {
|
|
642 expr = lparam;
|
|
643
|
|
644 foreach (var ex in exprs)
|
|
645 {
|
|
646 var type = typeof(ExpressionHoder<,>).MakeGenericType(expr.Type, ex.Type);
|
|
647 var fields = type.GetFields();
|
|
648
|
|
649 expr = Expression.MemberInit(
|
|
650 Expression.New(type),
|
|
651 Expression.Bind(fields[0], expr),
|
|
652 Expression.Bind(fields[1], ex));
|
|
653 }
|
|
654
|
|
655 var dic = new Dictionary<Expression, Expression>();
|
|
656 var parm = Expression.Parameter(expr.Type, lparam.Name);
|
|
657
|
|
658 for (var i = 0; i < exprs.Count; i++)
|
|
659 {
|
|
660 Expression ex = parm;
|
|
661
|
|
662 for (var j = i; j < exprs.Count - 1; j++)
|
|
663 ex = Expression.PropertyOrField(ex, "p");
|
|
664
|
|
665 ex = Expression.PropertyOrField(ex, "ex");
|
|
666
|
|
667 dic.Add(exprs[i], ex);
|
|
668
|
|
669 if (_subQueryExpressions == null)
|
|
670 _subQueryExpressions = new HashSet<Expression>();
|
|
671 _subQueryExpressions.Add(ex);
|
|
672 }
|
|
673
|
|
674 var newBody = lbody.Convert(ex =>
|
|
675 {
|
|
676 Expression e;
|
|
677 return dic.TryGetValue(ex, out e) ? e : ex;
|
|
678 });
|
|
679
|
|
680 var nparm = exprs.Aggregate<Expression,Expression>(parm, (c,t) => Expression.PropertyOrField(c, "p"));
|
|
681
|
|
682 newBody = newBody.Convert(ex => ex == lparam ? nparm : ex);
|
|
683
|
|
684 predicate = Expression.Lambda(newBody, parm);
|
|
685
|
|
686 var methodInfo = GetMethodInfo(method, "Select");
|
|
687
|
|
688 methodInfo = methodInfo.MakeGenericMethod(lparam.Type, expr.Type);
|
|
689 sequence = Expression.Call(methodInfo, sequence, Expression.Lambda(expr, lparam));
|
|
690 }
|
|
691
|
|
692 if (sequence != method.Arguments[0] || predicate != method.Arguments[1])
|
|
693 {
|
|
694 var methodInfo = method.Method.GetGenericMethodDefinition();
|
|
695 var genericType = sequence.Type.GetGenericArguments()[0];
|
|
696 var newMethod = methodInfo.MakeGenericMethod(genericType);
|
|
697
|
|
698 method = Expression.Call(newMethod, sequence, predicate);
|
|
699
|
|
700 if (exprs.Count > 0)
|
|
701 {
|
|
702 var parameter = Expression.Parameter(expr.Type, lparam.Name);
|
|
703
|
|
704 methodInfo = GetMethodInfo(method, "Select");
|
|
705 methodInfo = methodInfo.MakeGenericMethod(expr.Type, lparam.Type);
|
|
706 method = Expression.Call(methodInfo, method,
|
|
707 Expression.Lambda(
|
|
708 exprs.Aggregate((Expression)parameter, (current,_) => Expression.PropertyOrField(current, "p")),
|
|
709 parameter));
|
|
710 }
|
|
711 }
|
|
712
|
|
713 return method;
|
|
714 }
|
|
715
|
|
716 #endregion
|
|
717
|
|
718 #region ConvertGroupBy
|
|
719
|
|
720 public class GroupSubQuery<TKey,TElement>
|
|
721 {
|
|
722 public TKey Key;
|
|
723 public TElement Element;
|
|
724 }
|
|
725
|
|
726 interface IGroupByHelper
|
|
727 {
|
|
728 void Set(bool wrapInSubQuery, Expression sourceExpression, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector);
|
|
729
|
|
730 Expression AddElementSelectorQ ();
|
|
731 Expression AddElementSelectorE ();
|
|
732 Expression AddResultQ ();
|
|
733 Expression AddResultE ();
|
|
734 Expression WrapInSubQueryQ ();
|
|
735 Expression WrapInSubQueryE ();
|
|
736 Expression WrapInSubQueryResultQ();
|
|
737 Expression WrapInSubQueryResultE();
|
|
738 }
|
|
739
|
|
740 class GroupByHelper<TSource,TKey,TElement,TResult> : IGroupByHelper
|
|
741 {
|
|
742 bool _wrapInSubQuery;
|
|
743 Expression _sourceExpression;
|
|
744 LambdaExpression _keySelector;
|
|
745 LambdaExpression _elementSelector;
|
|
746 LambdaExpression _resultSelector;
|
|
747
|
|
748 public void Set(
|
|
749 bool wrapInSubQuery,
|
|
750 Expression sourceExpression,
|
|
751 LambdaExpression keySelector,
|
|
752 LambdaExpression elementSelector,
|
|
753 LambdaExpression resultSelector)
|
|
754 {
|
|
755 _wrapInSubQuery = wrapInSubQuery;
|
|
756 _sourceExpression = sourceExpression;
|
|
757 _keySelector = keySelector;
|
|
758 _elementSelector = elementSelector;
|
|
759 _resultSelector = resultSelector;
|
|
760 }
|
|
761
|
|
762 public Expression AddElementSelectorQ()
|
|
763 {
|
|
764 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<IGrouping<TKey,TSource>>>> func = (source,key,e,r) => source
|
|
765 .GroupBy(keyParam => key, _ => _)
|
|
766 ;
|
|
767
|
|
768 var body = func.Body.Unwrap();
|
|
769 var keyArg = GetLambda(body, 1).Parameters[0]; // .GroupBy(keyParam
|
|
770
|
|
771 return Convert(func, keyArg, null, null);
|
|
772 }
|
|
773
|
|
774 public Expression AddElementSelectorE()
|
|
775 {
|
|
776 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<IGrouping<TKey,TSource>>>> func = (source,key,e,r) => source
|
|
777 .GroupBy(keyParam => key, _ => _)
|
|
778 ;
|
|
779
|
|
780 var body = func.Body.Unwrap();
|
|
781 var keyArg = GetLambda(body, 1).Parameters[0]; // .GroupBy(keyParam
|
|
782
|
|
783 return Convert(func, keyArg, null, null);
|
|
784 }
|
|
785
|
|
786 public Expression AddResultQ()
|
|
787 {
|
|
788 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<TResult>>> func = (source,key,e,r) => source
|
|
789 .GroupBy(keyParam => key, elemParam => e)
|
|
790 .Select (resParam => r)
|
|
791 ;
|
|
792
|
|
793 var body = func.Body.Unwrap();
|
|
794 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .GroupBy(keyParam
|
|
795 var elemArg = GetLambda(body, 0, 2).Parameters[0]; // .GroupBy(..., elemParam
|
|
796 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam
|
|
797
|
|
798 return Convert(func, keyArg, elemArg, resArg);
|
|
799 }
|
|
800
|
|
801 public Expression AddResultE()
|
|
802 {
|
|
803 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<TResult>>> func = (source,key,e,r) => source
|
|
804 .GroupBy(keyParam => key, elemParam => e)
|
|
805 .Select (resParam => r)
|
|
806 ;
|
|
807
|
|
808 var body = func.Body.Unwrap();
|
|
809 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .GroupBy(keyParam
|
|
810 var elemArg = GetLambda(body, 0, 2).Parameters[0]; // .GroupBy(..., elemParam
|
|
811 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam
|
|
812
|
|
813 return Convert(func, keyArg, elemArg, resArg);
|
|
814 }
|
|
815
|
|
816 public Expression WrapInSubQueryQ()
|
|
817 {
|
|
818 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<IGrouping<TKey,TElement>>>> func = (source,key,e,r) => source
|
|
819 .Select(selectParam => new GroupSubQuery<TKey,TSource>
|
|
820 {
|
|
821 Key = key,
|
|
822 Element = selectParam
|
|
823 })
|
|
824 .GroupBy(_ => _.Key, elemParam => e)
|
|
825 ;
|
|
826
|
|
827 var body = func.Body.Unwrap();
|
|
828 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .Select (selectParam
|
|
829 var elemArg = GetLambda(body, 2). Parameters[0]; // .GroupBy(..., elemParam
|
|
830
|
|
831 return Convert(func, keyArg, elemArg, null);
|
|
832 }
|
|
833
|
|
834 public Expression WrapInSubQueryE()
|
|
835 {
|
|
836 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<IGrouping<TKey,TElement>>>> func = (source,key,e,r) => source
|
|
837 .Select(selectParam => new GroupSubQuery<TKey,TSource>
|
|
838 {
|
|
839 Key = key,
|
|
840 Element = selectParam
|
|
841 })
|
|
842 .GroupBy(_ => _.Key, elemParam => e)
|
|
843 ;
|
|
844
|
|
845 var body = func.Body.Unwrap();
|
|
846 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .Select (selectParam
|
|
847 var elemArg = GetLambda(body, 2). Parameters[0]; // .GroupBy(..., elemParam
|
|
848
|
|
849 return Convert(func, keyArg, elemArg, null);
|
|
850 }
|
|
851
|
|
852 public Expression WrapInSubQueryResultQ()
|
|
853 {
|
|
854 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<TResult>>> func = (source,key,e,r) => source
|
|
855 .Select(selectParam => new GroupSubQuery<TKey,TSource>
|
|
856 {
|
|
857 Key = key,
|
|
858 Element = selectParam
|
|
859 })
|
|
860 .GroupBy(_ => _.Key, elemParam => e)
|
|
861 .Select (resParam => r)
|
|
862 ;
|
|
863
|
|
864 var body = func.Body.Unwrap();
|
|
865 var keyArg = GetLambda(body, 0, 0, 1).Parameters[0]; // .Select (selectParam
|
|
866 var elemArg = GetLambda(body, 0, 2). Parameters[0]; // .GroupBy(..., elemParam
|
|
867 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam
|
|
868
|
|
869 return Convert(func, keyArg, elemArg, resArg);
|
|
870 }
|
|
871
|
|
872 public Expression WrapInSubQueryResultE()
|
|
873 {
|
|
874 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<TResult>>> func = (source,key,e,r) => source
|
|
875 .Select(selectParam => new GroupSubQuery<TKey,TSource>
|
|
876 {
|
|
877 Key = key,
|
|
878 Element = selectParam
|
|
879 })
|
|
880 .GroupBy(_ => _.Key, elemParam => e)
|
|
881 .Select (resParam => r)
|
|
882 ;
|
|
883
|
|
884 var body = func.Body.Unwrap();
|
|
885 var keyArg = GetLambda(body, 0, 0, 1).Parameters[0]; // .Select (selectParam
|
|
886 var elemArg = GetLambda(body, 0, 2). Parameters[0]; // .GroupBy(..., elemParam
|
|
887 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam
|
|
888
|
|
889 return Convert(func, keyArg, elemArg, resArg);
|
|
890 }
|
|
891
|
|
892 Expression Convert(
|
|
893 LambdaExpression func,
|
|
894 ParameterExpression keyArg,
|
|
895 ParameterExpression elemArg,
|
|
896 ParameterExpression resArg)
|
|
897 {
|
|
898 var body = func.Body.Unwrap();
|
|
899 var expr = body.Convert(ex =>
|
|
900 {
|
|
901 if (ex == func.Parameters[0])
|
|
902 return _sourceExpression;
|
|
903
|
|
904 if (ex == func.Parameters[1])
|
|
905 return _keySelector.Body.Convert(e => e == _keySelector.Parameters[0] ? keyArg : e);
|
|
906
|
|
907 if (ex == func.Parameters[2])
|
|
908 {
|
|
909 Expression obj = elemArg;
|
|
910
|
|
911 if (_wrapInSubQuery)
|
|
912 obj = Expression.PropertyOrField(elemArg, "Element");
|
|
913
|
|
914 if (_elementSelector == null)
|
|
915 return obj;
|
|
916
|
|
917 return _elementSelector.Body.Convert(e => e == _elementSelector.Parameters[0] ? obj : e);
|
|
918 }
|
|
919
|
|
920 if (ex == func.Parameters[3])
|
|
921 return _resultSelector.Body.Convert(e =>
|
|
922 {
|
|
923 if (e == _resultSelector.Parameters[0])
|
|
924 return Expression.PropertyOrField(resArg, "Key");
|
|
925
|
|
926 if (e == _resultSelector.Parameters[1])
|
|
927 return resArg;
|
|
928
|
|
929 return e;
|
|
930 });
|
|
931
|
|
932 return ex;
|
|
933 });
|
|
934
|
|
935 return expr;
|
|
936 }
|
|
937 }
|
|
938
|
|
939 static LambdaExpression GetLambda(Expression expression, params int[] n)
|
|
940 {
|
|
941 foreach (var i in n)
|
|
942 expression = ((MethodCallExpression)expression).Arguments[i].Unwrap();
|
|
943 return (LambdaExpression)expression;
|
|
944 }
|
|
945
|
|
946 Expression ConvertGroupBy(MethodCallExpression method)
|
|
947 {
|
|
948 if (method.Arguments[method.Arguments.Count - 1].Unwrap().NodeType != ExpressionType.Lambda)
|
|
949 return method;
|
|
950
|
|
951 var types = method.Method.GetGenericMethodDefinition().GetGenericArguments()
|
|
952 .Zip(method.Method.GetGenericArguments(), (n, t) => new { n = n.Name, t })
|
|
953 .ToDictionary(_ => _.n, _ => _.t);
|
|
954
|
|
955 var sourceExpression = OptimizeExpression(method.Arguments[0].Unwrap());
|
|
956 var keySelector = (LambdaExpression)OptimizeExpression(method.Arguments[1].Unwrap());
|
|
957 var elementSelector = types.ContainsKey("TElement") ? (LambdaExpression)OptimizeExpression(method.Arguments[2].Unwrap()) : null;
|
|
958 var resultSelector = types.ContainsKey("TResult") ?
|
|
959 (LambdaExpression)OptimizeExpression(method.Arguments[types.ContainsKey("TElement") ? 3 : 2].Unwrap()) : null;
|
|
960
|
|
961 var needSubQuery = null != ConvertExpression(keySelector.Body.Unwrap()).Find(IsExpression);
|
|
962
|
|
963 if (!needSubQuery && resultSelector == null && elementSelector != null)
|
|
964 return method;
|
|
965
|
|
966 var gtype = typeof(GroupByHelper<,,,>).MakeGenericType(
|
|
967 types["TSource"],
|
|
968 types["TKey"],
|
|
969 types.ContainsKey("TElement") ? types["TElement"] : types["TSource"],
|
|
970 types.ContainsKey("TResult") ? types["TResult"] : types["TSource"]);
|
|
971
|
|
972 var helper =
|
|
973 //Expression.Lambda<Func<IGroupByHelper>>(
|
|
974 // Expression.Convert(Expression.New(gtype), typeof(IGroupByHelper)))
|
|
975 //.Compile()();
|
|
976 (IGroupByHelper)Activator.CreateInstance(gtype);
|
|
977
|
|
978 helper.Set(needSubQuery, sourceExpression, keySelector, elementSelector, resultSelector);
|
|
979
|
|
980 if (method.Method.DeclaringType == typeof(Queryable))
|
|
981 {
|
|
982 if (!needSubQuery)
|
|
983 return resultSelector == null ? helper.AddElementSelectorQ() : helper.AddResultQ();
|
|
984
|
|
985 return resultSelector == null ? helper.WrapInSubQueryQ() : helper.WrapInSubQueryResultQ();
|
|
986 }
|
|
987 else
|
|
988 {
|
|
989 if (!needSubQuery)
|
|
990 return resultSelector == null ? helper.AddElementSelectorE() : helper.AddResultE();
|
|
991
|
|
992 return resultSelector == null ? helper.WrapInSubQueryE() : helper.WrapInSubQueryResultE();
|
|
993 }
|
|
994 }
|
|
995
|
|
996 bool IsExpression(Expression ex)
|
|
997 {
|
|
998 switch (ex.NodeType)
|
|
999 {
|
|
1000 case ExpressionType.Convert :
|
|
1001 case ExpressionType.ConvertChecked :
|
|
1002 case ExpressionType.MemberInit :
|
|
1003 case ExpressionType.New :
|
|
1004 case ExpressionType.NewArrayBounds :
|
|
1005 case ExpressionType.NewArrayInit :
|
|
1006 case ExpressionType.Parameter : return false;
|
|
1007 case ExpressionType.MemberAccess :
|
|
1008 {
|
|
1009 var ma = (MemberExpression)ex;
|
|
1010 var attr = GetFunctionAttribute(ma.Member);
|
|
1011
|
|
1012 if (attr != null)
|
|
1013 return true;
|
|
1014
|
|
1015 return false;
|
|
1016 }
|
|
1017 }
|
|
1018
|
|
1019 return true;
|
|
1020 }
|
|
1021
|
|
1022 #endregion
|
|
1023
|
|
1024 #region ConvertSelectMany
|
|
1025
|
|
1026 interface ISelectManyHelper
|
|
1027 {
|
|
1028 void Set(Expression sourceExpression, LambdaExpression colSelector);
|
|
1029
|
|
1030 Expression AddElementSelectorQ();
|
|
1031 Expression AddElementSelectorE();
|
|
1032 }
|
|
1033
|
|
1034 class SelectManyHelper<TSource,TCollection> : ISelectManyHelper
|
|
1035 {
|
|
1036 Expression _sourceExpression;
|
|
1037 LambdaExpression _colSelector;
|
|
1038
|
|
1039 public void Set(Expression sourceExpression, LambdaExpression colSelector)
|
|
1040 {
|
|
1041 _sourceExpression = sourceExpression;
|
|
1042 _colSelector = colSelector;
|
|
1043 }
|
|
1044
|
|
1045 public Expression AddElementSelectorQ()
|
|
1046 {
|
|
1047 Expression<Func<IQueryable<TSource>,IEnumerable<TCollection>,IQueryable<TCollection>>> func = (source,col) => source
|
|
1048 .SelectMany(colParam => col, (s,c) => c)
|
|
1049 ;
|
|
1050
|
|
1051 var body = func.Body.Unwrap();
|
|
1052 var colArg = GetLambda(body, 1).Parameters[0]; // .SelectMany(colParam
|
|
1053
|
|
1054 return Convert(func, colArg);
|
|
1055 }
|
|
1056
|
|
1057 public Expression AddElementSelectorE()
|
|
1058 {
|
|
1059 Expression<Func<IEnumerable<TSource>,IEnumerable<TCollection>,IEnumerable<TCollection>>> func = (source,col) => source
|
|
1060 .SelectMany(colParam => col, (s,c) => c)
|
|
1061 ;
|
|
1062
|
|
1063 var body = func.Body.Unwrap();
|
|
1064 var colArg = GetLambda(body, 1).Parameters[0]; // .SelectMany(colParam
|
|
1065
|
|
1066 return Convert(func, colArg);
|
|
1067 }
|
|
1068
|
|
1069 Expression Convert(LambdaExpression func, ParameterExpression colArg)
|
|
1070 {
|
|
1071 var body = func.Body.Unwrap();
|
|
1072 var expr = body.Convert(ex =>
|
|
1073 {
|
|
1074 if (ex == func.Parameters[0])
|
|
1075 return _sourceExpression;
|
|
1076
|
|
1077 if (ex == func.Parameters[1])
|
|
1078 return _colSelector.Body.Convert(e => e == _colSelector.Parameters[0] ? colArg : e);
|
|
1079
|
|
1080 return ex;
|
|
1081 });
|
|
1082
|
|
1083 return expr;
|
|
1084 }
|
|
1085 }
|
|
1086
|
|
1087 Expression ConvertSelectMany(MethodCallExpression method)
|
|
1088 {
|
|
1089 if (method.Arguments.Count != 2 || ((LambdaExpression)method.Arguments[1].Unwrap()).Parameters.Count != 1)
|
|
1090 return method;
|
|
1091
|
|
1092 var types = method.Method.GetGenericMethodDefinition().GetGenericArguments()
|
|
1093 .Zip(method.Method.GetGenericArguments(), (n, t) => new { n = n.Name, t })
|
|
1094 .ToDictionary(_ => _.n, _ => _.t);
|
|
1095
|
|
1096 var sourceExpression = OptimizeExpression(method.Arguments[0].Unwrap());
|
|
1097 var colSelector = (LambdaExpression)OptimizeExpression(method.Arguments[1].Unwrap());
|
|
1098
|
|
1099 var gtype = typeof(SelectManyHelper<,>).MakeGenericType(types["TSource"], types["TResult"]);
|
|
1100 var helper =
|
|
1101 //Expression.Lambda<Func<ISelectManyHelper>>(
|
|
1102 // Expression.Convert(Expression.New(gtype), typeof(ISelectManyHelper)))
|
|
1103 //.Compile()();
|
|
1104 (ISelectManyHelper)Activator.CreateInstance(gtype);
|
|
1105
|
|
1106 helper.Set(sourceExpression, colSelector);
|
|
1107
|
|
1108 return method.Method.DeclaringType == typeof(Queryable) ?
|
|
1109 helper.AddElementSelectorQ() :
|
|
1110 helper.AddElementSelectorE();
|
|
1111 }
|
|
1112
|
|
1113 #endregion
|
|
1114
|
|
1115 #region ConvertPredicate
|
|
1116
|
|
1117 Expression ConvertPredicate(MethodCallExpression method)
|
|
1118 {
|
|
1119 if (method.Arguments.Count != 2)
|
|
1120 return method;
|
|
1121
|
|
1122 var cm = GetQueriableMethodInfo(method, (m,_) => m.Name == method.Method.Name && m.GetParameters().Length == 1);
|
|
1123 var wm = GetMethodInfo(method, "Where");
|
|
1124
|
|
1125 var argType = method.Method.GetGenericArguments()[0];
|
|
1126
|
|
1127 wm = wm.MakeGenericMethod(argType);
|
|
1128 cm = cm.MakeGenericMethod(argType);
|
|
1129
|
|
1130 return Expression.Call(null, cm,
|
|
1131 Expression.Call(null, wm,
|
|
1132 OptimizeExpression(method.Arguments[0]),
|
|
1133 OptimizeExpression(method.Arguments[1])));
|
|
1134 }
|
|
1135
|
|
1136 #endregion
|
|
1137
|
|
1138 #region ConvertSelector
|
|
1139
|
|
1140 Expression ConvertSelector(MethodCallExpression method, bool isGeneric)
|
|
1141 {
|
|
1142 if (method.Arguments.Count != 2)
|
|
1143 return method;
|
|
1144
|
|
1145 isGeneric = isGeneric && method.Method.DeclaringType == typeof(Queryable);
|
|
1146
|
|
1147 var types = GetMethodGenericTypes(method);
|
|
1148 var sm = GetMethodInfo(method, "Select");
|
|
1149 var cm = GetQueriableMethodInfo(method, (m,isDefault) =>
|
|
1150 {
|
|
1151 if (m.Name == method.Method.Name)
|
|
1152 {
|
|
1153 var ps = m.GetParameters();
|
|
1154
|
|
1155 if (ps.Length == 1)
|
|
1156 {
|
|
1157 if (isGeneric)
|
|
1158 return true;
|
|
1159
|
|
1160 var ts = ps[0].ParameterType.GetGenericArguments();
|
|
1161 return ts[0] == types[1] || isDefault && ts[0].IsGenericParameter;
|
|
1162 }
|
|
1163 }
|
|
1164
|
|
1165 return false;
|
|
1166 });
|
|
1167
|
|
1168 var argType = types[0];
|
|
1169
|
|
1170 sm = sm.MakeGenericMethod(argType, types[1]);
|
|
1171
|
|
1172 if (cm.IsGenericMethodDefinition)
|
|
1173 cm = cm.MakeGenericMethod(types[1]);
|
|
1174
|
|
1175 return Expression.Call(null, cm,
|
|
1176 OptimizeExpression(Expression.Call(null, sm,
|
|
1177 method.Arguments[0],
|
|
1178 method.Arguments[1])));
|
|
1179 }
|
|
1180
|
|
1181 #endregion
|
|
1182
|
|
1183 #region ConvertSelect
|
|
1184
|
|
1185 Expression ConvertSelect(MethodCallExpression method)
|
|
1186 {
|
|
1187 var sequence = OptimizeExpression(method.Arguments[0]);
|
|
1188 var lambda1 = (LambdaExpression)method.Arguments[1].Unwrap();
|
|
1189 var lambda = (LambdaExpression)OptimizeExpression(lambda1);
|
|
1190
|
|
1191 if (lambda1.Parameters.Count > 1 ||
|
|
1192 sequence.NodeType != ExpressionType.Call ||
|
|
1193 ((MethodCallExpression)sequence).Method.Name != method.Method.Name)
|
|
1194 {
|
|
1195 return method;
|
|
1196 }
|
|
1197
|
|
1198 var slambda = (LambdaExpression)((MethodCallExpression)sequence).Arguments[1].Unwrap();
|
|
1199 var sbody = slambda.Body.Unwrap();
|
|
1200
|
|
1201 if (slambda.Parameters.Count > 1 || sbody.NodeType != ExpressionType.MemberAccess)
|
|
1202 return method;
|
|
1203
|
|
1204 var types1 = GetMethodGenericTypes((MethodCallExpression)sequence);
|
|
1205 var types2 = GetMethodGenericTypes(method);
|
|
1206
|
|
1207 var expr = Expression.Call(null,
|
|
1208 GetMethodInfo(method, "Select").MakeGenericMethod(types1[0], types2[1]),
|
|
1209 ((MethodCallExpression)sequence).Arguments[0],
|
|
1210 Expression.Lambda(
|
|
1211 lambda.Body.Convert(ex => ex == lambda.Parameters[0] ? sbody : ex),
|
|
1212 slambda.Parameters[0]));
|
|
1213
|
|
1214 return expr;
|
|
1215 }
|
|
1216
|
|
1217 #endregion
|
|
1218
|
|
1219 #region ConvertIQueriable
|
|
1220
|
|
1221 Expression ConvertIQueriable(Expression expression)
|
|
1222 {
|
|
1223 if (expression.NodeType == ExpressionType.MemberAccess || expression.NodeType == ExpressionType.Call)
|
|
1224 {
|
|
1225 var p = Expression.Parameter(typeof(Expression), "exp");
|
|
1226 var exas = expression.GetExpressionAccessors(p);
|
|
1227 var expr = ReplaceParameter(exas, expression, _ => {});
|
|
1228
|
|
1229 if (expr.Find(e => e.NodeType == ExpressionType.Parameter && e != p) != null)
|
|
1230 return expression;
|
|
1231
|
|
1232 var l = Expression.Lambda<Func<Expression,IQueryable>>(Expression.Convert(expr, typeof(IQueryable)), new [] { p });
|
|
1233 var n = _query.AddQueryableAccessors(expression, l);
|
|
1234
|
|
1235 Expression accessor;
|
|
1236
|
|
1237 _expressionAccessors.TryGetValue(expression, out accessor);
|
|
1238
|
|
1239 var path =
|
|
1240 Expression.Call(
|
|
1241 Expression.Constant(_query),
|
|
1242 ReflectionHelper.Expressor<Query>.MethodExpressor(a => a.GetIQueryable(0, null)),
|
|
1243 new[] { Expression.Constant(n), accessor ?? Expression.Constant(null, typeof(Expression)) });
|
|
1244
|
|
1245 var qex = _query.GetIQueryable(n, expression);
|
|
1246
|
|
1247 if (expression.NodeType == ExpressionType.Call && qex.NodeType == ExpressionType.Call)
|
|
1248 {
|
|
1249 var m1 = (MethodCallExpression)expression;
|
|
1250 var m2 = (MethodCallExpression)qex;
|
|
1251
|
|
1252 if (m1.Method == m2.Method)
|
|
1253 return expression;
|
|
1254 }
|
|
1255
|
|
1256 foreach (var a in qex.GetExpressionAccessors(path))
|
|
1257 if (!_expressionAccessors.ContainsKey(a.Key))
|
|
1258 _expressionAccessors.Add(a.Key, a.Value);
|
|
1259
|
|
1260 return qex;
|
|
1261 }
|
|
1262
|
|
1263 throw new InvalidOperationException();
|
|
1264 }
|
|
1265
|
|
1266 #endregion
|
|
1267
|
|
1268 #region ConvertElementAt
|
|
1269
|
|
1270 Expression ConvertElementAt(MethodCallExpression method)
|
|
1271 {
|
|
1272 var sequence = OptimizeExpression(method.Arguments[0]);
|
|
1273 var index = OptimizeExpression(method.Arguments[1]).Unwrap();
|
|
1274 var sourceType = method.Method.GetGenericArguments()[0];
|
|
1275
|
|
1276 MethodInfo skipMethod;
|
|
1277
|
|
1278 if (index.NodeType == ExpressionType.Lambda)
|
|
1279 {
|
|
1280 skipMethod = ReflectionHelper.Expressor<object>.MethodExpressor(o => LinqExtensions.Skip<object>(null, null));
|
|
1281 skipMethod = skipMethod.GetGenericMethodDefinition();
|
|
1282 }
|
|
1283 else
|
|
1284 {
|
|
1285 skipMethod = GetQueriableMethodInfo(method, (mi,_) => mi.Name == "Skip");
|
|
1286 }
|
|
1287
|
|
1288 skipMethod = skipMethod.MakeGenericMethod(sourceType);
|
|
1289
|
|
1290 var methodName = method.Method.Name == "ElementAt" ? "First" : "FirstOrDefault";
|
|
1291 var firstMethod = GetQueriableMethodInfo(method, (mi,_) => mi.Name == methodName && mi.GetParameters().Length == 1);
|
|
1292
|
|
1293 firstMethod = firstMethod.MakeGenericMethod(sourceType);
|
|
1294
|
|
1295 return Expression.Call(null, firstMethod, Expression.Call(null, skipMethod, sequence, index));
|
|
1296 }
|
|
1297
|
|
1298 #endregion
|
|
1299
|
|
1300 #region Helpers
|
|
1301
|
|
1302 MethodInfo GetQueriableMethodInfo(MethodCallExpression method, Func<MethodInfo,bool,bool> predicate)
|
|
1303 {
|
|
1304 return method.Method.DeclaringType == typeof(Enumerable) ?
|
|
1305 EnumerableMethods.FirstOrDefault(m => predicate(m, false)) ?? EnumerableMethods.First(m => predicate(m, true)):
|
|
1306 QueryableMethods. FirstOrDefault(m => predicate(m, false)) ?? QueryableMethods. First(m => predicate(m, true));
|
|
1307 }
|
|
1308
|
|
1309 MethodInfo GetMethodInfo(MethodCallExpression method, string name)
|
|
1310 {
|
|
1311 return method.Method.DeclaringType == typeof(Enumerable) ?
|
|
1312 EnumerableMethods
|
|
1313 .Where(m => m.Name == name && m.GetParameters().Length == 2)
|
|
1314 .First(m => m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2) :
|
|
1315 QueryableMethods
|
|
1316 .Where(m => m.Name == name && m.GetParameters().Length == 2)
|
|
1317 .First(m => m.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2);
|
|
1318 }
|
|
1319
|
|
1320 static Type[] GetMethodGenericTypes(MethodCallExpression method)
|
|
1321 {
|
|
1322 return method.Method.DeclaringType == typeof(Enumerable) ?
|
|
1323 method.Method.GetParameters()[1].ParameterType.GetGenericArguments() :
|
|
1324 method.Method.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments();
|
|
1325 }
|
|
1326
|
|
1327 #endregion
|
|
1328
|
|
1329 #endregion
|
|
1330 }
|
|
1331 }
|