Mercurial > pub > bltoolkit
annotate Source/Data/Linq/Builder/ExpressionBuilder.cs @ 9:1e85f66cf767 default tip
update bltoolkit
author | nickolay |
---|---|
date | Thu, 05 Apr 2018 20:53:26 +0300 |
parents | 11b6da379593 |
children |
rev | line source |
---|---|
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(); | |
6
11b6da379593
Исправлена странная ошибка при использовании OfType<...>().Where(...)
cin
parents:
5
diff
changeset
|
695 var queryableType = sequence.Type.GetInterface(typeof(IEnumerable<>).Name); |
5
f7d63a092920
Исправлено условие Where в тех случаях, когда репозитарий не является генериком
cin
parents:
0
diff
changeset
|
696 var genericType = queryableType.GetGenericArguments()[0]; |
0 | 697 var newMethod = methodInfo.MakeGenericMethod(genericType); |
698 | |
699 method = Expression.Call(newMethod, sequence, predicate); | |
700 | |
701 if (exprs.Count > 0) | |
702 { | |
703 var parameter = Expression.Parameter(expr.Type, lparam.Name); | |
704 | |
705 methodInfo = GetMethodInfo(method, "Select"); | |
706 methodInfo = methodInfo.MakeGenericMethod(expr.Type, lparam.Type); | |
707 method = Expression.Call(methodInfo, method, | |
708 Expression.Lambda( | |
709 exprs.Aggregate((Expression)parameter, (current,_) => Expression.PropertyOrField(current, "p")), | |
710 parameter)); | |
711 } | |
712 } | |
713 | |
714 return method; | |
715 } | |
716 | |
717 #endregion | |
718 | |
719 #region ConvertGroupBy | |
720 | |
721 public class GroupSubQuery<TKey,TElement> | |
722 { | |
723 public TKey Key; | |
724 public TElement Element; | |
725 } | |
726 | |
727 interface IGroupByHelper | |
728 { | |
729 void Set(bool wrapInSubQuery, Expression sourceExpression, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector); | |
730 | |
731 Expression AddElementSelectorQ (); | |
732 Expression AddElementSelectorE (); | |
733 Expression AddResultQ (); | |
734 Expression AddResultE (); | |
735 Expression WrapInSubQueryQ (); | |
736 Expression WrapInSubQueryE (); | |
737 Expression WrapInSubQueryResultQ(); | |
738 Expression WrapInSubQueryResultE(); | |
739 } | |
740 | |
741 class GroupByHelper<TSource,TKey,TElement,TResult> : IGroupByHelper | |
742 { | |
743 bool _wrapInSubQuery; | |
744 Expression _sourceExpression; | |
745 LambdaExpression _keySelector; | |
746 LambdaExpression _elementSelector; | |
747 LambdaExpression _resultSelector; | |
748 | |
749 public void Set( | |
750 bool wrapInSubQuery, | |
751 Expression sourceExpression, | |
752 LambdaExpression keySelector, | |
753 LambdaExpression elementSelector, | |
754 LambdaExpression resultSelector) | |
755 { | |
756 _wrapInSubQuery = wrapInSubQuery; | |
757 _sourceExpression = sourceExpression; | |
758 _keySelector = keySelector; | |
759 _elementSelector = elementSelector; | |
760 _resultSelector = resultSelector; | |
761 } | |
762 | |
763 public Expression AddElementSelectorQ() | |
764 { | |
765 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<IGrouping<TKey,TSource>>>> func = (source,key,e,r) => source | |
766 .GroupBy(keyParam => key, _ => _) | |
767 ; | |
768 | |
769 var body = func.Body.Unwrap(); | |
770 var keyArg = GetLambda(body, 1).Parameters[0]; // .GroupBy(keyParam | |
771 | |
772 return Convert(func, keyArg, null, null); | |
773 } | |
774 | |
775 public Expression AddElementSelectorE() | |
776 { | |
777 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<IGrouping<TKey,TSource>>>> func = (source,key,e,r) => source | |
778 .GroupBy(keyParam => key, _ => _) | |
779 ; | |
780 | |
781 var body = func.Body.Unwrap(); | |
782 var keyArg = GetLambda(body, 1).Parameters[0]; // .GroupBy(keyParam | |
783 | |
784 return Convert(func, keyArg, null, null); | |
785 } | |
786 | |
787 public Expression AddResultQ() | |
788 { | |
789 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<TResult>>> func = (source,key,e,r) => source | |
790 .GroupBy(keyParam => key, elemParam => e) | |
791 .Select (resParam => r) | |
792 ; | |
793 | |
794 var body = func.Body.Unwrap(); | |
795 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .GroupBy(keyParam | |
796 var elemArg = GetLambda(body, 0, 2).Parameters[0]; // .GroupBy(..., elemParam | |
797 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam | |
798 | |
799 return Convert(func, keyArg, elemArg, resArg); | |
800 } | |
801 | |
802 public Expression AddResultE() | |
803 { | |
804 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<TResult>>> func = (source,key,e,r) => source | |
805 .GroupBy(keyParam => key, elemParam => e) | |
806 .Select (resParam => r) | |
807 ; | |
808 | |
809 var body = func.Body.Unwrap(); | |
810 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .GroupBy(keyParam | |
811 var elemArg = GetLambda(body, 0, 2).Parameters[0]; // .GroupBy(..., elemParam | |
812 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam | |
813 | |
814 return Convert(func, keyArg, elemArg, resArg); | |
815 } | |
816 | |
817 public Expression WrapInSubQueryQ() | |
818 { | |
819 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<IGrouping<TKey,TElement>>>> func = (source,key,e,r) => source | |
820 .Select(selectParam => new GroupSubQuery<TKey,TSource> | |
821 { | |
822 Key = key, | |
823 Element = selectParam | |
824 }) | |
825 .GroupBy(_ => _.Key, elemParam => e) | |
826 ; | |
827 | |
828 var body = func.Body.Unwrap(); | |
829 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .Select (selectParam | |
830 var elemArg = GetLambda(body, 2). Parameters[0]; // .GroupBy(..., elemParam | |
831 | |
832 return Convert(func, keyArg, elemArg, null); | |
833 } | |
834 | |
835 public Expression WrapInSubQueryE() | |
836 { | |
837 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<IGrouping<TKey,TElement>>>> func = (source,key,e,r) => source | |
838 .Select(selectParam => new GroupSubQuery<TKey,TSource> | |
839 { | |
840 Key = key, | |
841 Element = selectParam | |
842 }) | |
843 .GroupBy(_ => _.Key, elemParam => e) | |
844 ; | |
845 | |
846 var body = func.Body.Unwrap(); | |
847 var keyArg = GetLambda(body, 0, 1).Parameters[0]; // .Select (selectParam | |
848 var elemArg = GetLambda(body, 2). Parameters[0]; // .GroupBy(..., elemParam | |
849 | |
850 return Convert(func, keyArg, elemArg, null); | |
851 } | |
852 | |
853 public Expression WrapInSubQueryResultQ() | |
854 { | |
855 Expression<Func<IQueryable<TSource>,TKey,TElement,TResult,IQueryable<TResult>>> func = (source,key,e,r) => source | |
856 .Select(selectParam => new GroupSubQuery<TKey,TSource> | |
857 { | |
858 Key = key, | |
859 Element = selectParam | |
860 }) | |
861 .GroupBy(_ => _.Key, elemParam => e) | |
862 .Select (resParam => r) | |
863 ; | |
864 | |
865 var body = func.Body.Unwrap(); | |
866 var keyArg = GetLambda(body, 0, 0, 1).Parameters[0]; // .Select (selectParam | |
867 var elemArg = GetLambda(body, 0, 2). Parameters[0]; // .GroupBy(..., elemParam | |
868 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam | |
869 | |
870 return Convert(func, keyArg, elemArg, resArg); | |
871 } | |
872 | |
873 public Expression WrapInSubQueryResultE() | |
874 { | |
875 Expression<Func<IEnumerable<TSource>,TKey,TElement,TResult,IEnumerable<TResult>>> func = (source,key,e,r) => source | |
876 .Select(selectParam => new GroupSubQuery<TKey,TSource> | |
877 { | |
878 Key = key, | |
879 Element = selectParam | |
880 }) | |
881 .GroupBy(_ => _.Key, elemParam => e) | |
882 .Select (resParam => r) | |
883 ; | |
884 | |
885 var body = func.Body.Unwrap(); | |
886 var keyArg = GetLambda(body, 0, 0, 1).Parameters[0]; // .Select (selectParam | |
887 var elemArg = GetLambda(body, 0, 2). Parameters[0]; // .GroupBy(..., elemParam | |
888 var resArg = GetLambda(body, 1). Parameters[0]; // .Select (resParam | |
889 | |
890 return Convert(func, keyArg, elemArg, resArg); | |
891 } | |
892 | |
893 Expression Convert( | |
894 LambdaExpression func, | |
895 ParameterExpression keyArg, | |
896 ParameterExpression elemArg, | |
897 ParameterExpression resArg) | |
898 { | |
899 var body = func.Body.Unwrap(); | |
900 var expr = body.Convert(ex => | |
901 { | |
902 if (ex == func.Parameters[0]) | |
903 return _sourceExpression; | |
904 | |
905 if (ex == func.Parameters[1]) | |
906 return _keySelector.Body.Convert(e => e == _keySelector.Parameters[0] ? keyArg : e); | |
907 | |
908 if (ex == func.Parameters[2]) | |
909 { | |
910 Expression obj = elemArg; | |
911 | |
912 if (_wrapInSubQuery) | |
913 obj = Expression.PropertyOrField(elemArg, "Element"); | |
914 | |
915 if (_elementSelector == null) | |
916 return obj; | |
917 | |
918 return _elementSelector.Body.Convert(e => e == _elementSelector.Parameters[0] ? obj : e); | |
919 } | |
920 | |
921 if (ex == func.Parameters[3]) | |
922 return _resultSelector.Body.Convert(e => | |
923 { | |
924 if (e == _resultSelector.Parameters[0]) | |
925 return Expression.PropertyOrField(resArg, "Key"); | |
926 | |
927 if (e == _resultSelector.Parameters[1]) | |
928 return resArg; | |
929 | |
930 return e; | |
931 }); | |
932 | |
933 return ex; | |
934 }); | |
935 | |
936 return expr; | |
937 } | |
938 } | |
939 | |
940 static LambdaExpression GetLambda(Expression expression, params int[] n) | |
941 { | |
942 foreach (var i in n) | |
943 expression = ((MethodCallExpression)expression).Arguments[i].Unwrap(); | |
944 return (LambdaExpression)expression; | |
945 } | |
946 | |
947 Expression ConvertGroupBy(MethodCallExpression method) | |
948 { | |
949 if (method.Arguments[method.Arguments.Count - 1].Unwrap().NodeType != ExpressionType.Lambda) | |
950 return method; | |
951 | |
952 var types = method.Method.GetGenericMethodDefinition().GetGenericArguments() | |
953 .Zip(method.Method.GetGenericArguments(), (n, t) => new { n = n.Name, t }) | |
954 .ToDictionary(_ => _.n, _ => _.t); | |
955 | |
956 var sourceExpression = OptimizeExpression(method.Arguments[0].Unwrap()); | |
957 var keySelector = (LambdaExpression)OptimizeExpression(method.Arguments[1].Unwrap()); | |
958 var elementSelector = types.ContainsKey("TElement") ? (LambdaExpression)OptimizeExpression(method.Arguments[2].Unwrap()) : null; | |
959 var resultSelector = types.ContainsKey("TResult") ? | |
960 (LambdaExpression)OptimizeExpression(method.Arguments[types.ContainsKey("TElement") ? 3 : 2].Unwrap()) : null; | |
961 | |
962 var needSubQuery = null != ConvertExpression(keySelector.Body.Unwrap()).Find(IsExpression); | |
963 | |
964 if (!needSubQuery && resultSelector == null && elementSelector != null) | |
965 return method; | |
966 | |
967 var gtype = typeof(GroupByHelper<,,,>).MakeGenericType( | |
968 types["TSource"], | |
969 types["TKey"], | |
970 types.ContainsKey("TElement") ? types["TElement"] : types["TSource"], | |
971 types.ContainsKey("TResult") ? types["TResult"] : types["TSource"]); | |
972 | |
973 var helper = | |
974 //Expression.Lambda<Func<IGroupByHelper>>( | |
975 // Expression.Convert(Expression.New(gtype), typeof(IGroupByHelper))) | |
976 //.Compile()(); | |
977 (IGroupByHelper)Activator.CreateInstance(gtype); | |
978 | |
979 helper.Set(needSubQuery, sourceExpression, keySelector, elementSelector, resultSelector); | |
980 | |
981 if (method.Method.DeclaringType == typeof(Queryable)) | |
982 { | |
983 if (!needSubQuery) | |
984 return resultSelector == null ? helper.AddElementSelectorQ() : helper.AddResultQ(); | |
985 | |
986 return resultSelector == null ? helper.WrapInSubQueryQ() : helper.WrapInSubQueryResultQ(); | |
987 } | |
988 else | |
989 { | |
990 if (!needSubQuery) | |
991 return resultSelector == null ? helper.AddElementSelectorE() : helper.AddResultE(); | |
992 | |
993 return resultSelector == null ? helper.WrapInSubQueryE() : helper.WrapInSubQueryResultE(); | |
994 } | |
995 } | |
996 | |
997 bool IsExpression(Expression ex) | |
998 { | |
999 switch (ex.NodeType) | |
1000 { | |
1001 case ExpressionType.Convert : | |
1002 case ExpressionType.ConvertChecked : | |
1003 case ExpressionType.MemberInit : | |
1004 case ExpressionType.New : | |
1005 case ExpressionType.NewArrayBounds : | |
1006 case ExpressionType.NewArrayInit : | |
1007 case ExpressionType.Parameter : return false; | |
1008 case ExpressionType.MemberAccess : | |
1009 { | |
1010 var ma = (MemberExpression)ex; | |
1011 var attr = GetFunctionAttribute(ma.Member); | |
1012 | |
1013 if (attr != null) | |
1014 return true; | |
1015 | |
1016 return false; | |
1017 } | |
1018 } | |
1019 | |
1020 return true; | |
1021 } | |
1022 | |
1023 #endregion | |
1024 | |
1025 #region ConvertSelectMany | |
1026 | |
1027 interface ISelectManyHelper | |
1028 { | |
1029 void Set(Expression sourceExpression, LambdaExpression colSelector); | |
1030 | |
1031 Expression AddElementSelectorQ(); | |
1032 Expression AddElementSelectorE(); | |
1033 } | |
1034 | |
1035 class SelectManyHelper<TSource,TCollection> : ISelectManyHelper | |
1036 { | |
1037 Expression _sourceExpression; | |
1038 LambdaExpression _colSelector; | |
1039 | |
1040 public void Set(Expression sourceExpression, LambdaExpression colSelector) | |
1041 { | |
1042 _sourceExpression = sourceExpression; | |
1043 _colSelector = colSelector; | |
1044 } | |
1045 | |
1046 public Expression AddElementSelectorQ() | |
1047 { | |
1048 Expression<Func<IQueryable<TSource>,IEnumerable<TCollection>,IQueryable<TCollection>>> func = (source,col) => source | |
1049 .SelectMany(colParam => col, (s,c) => c) | |
1050 ; | |
1051 | |
1052 var body = func.Body.Unwrap(); | |
1053 var colArg = GetLambda(body, 1).Parameters[0]; // .SelectMany(colParam | |
1054 | |
1055 return Convert(func, colArg); | |
1056 } | |
1057 | |
1058 public Expression AddElementSelectorE() | |
1059 { | |
1060 Expression<Func<IEnumerable<TSource>,IEnumerable<TCollection>,IEnumerable<TCollection>>> func = (source,col) => source | |
1061 .SelectMany(colParam => col, (s,c) => c) | |
1062 ; | |
1063 | |
1064 var body = func.Body.Unwrap(); | |
1065 var colArg = GetLambda(body, 1).Parameters[0]; // .SelectMany(colParam | |
1066 | |
1067 return Convert(func, colArg); | |
1068 } | |
1069 | |
1070 Expression Convert(LambdaExpression func, ParameterExpression colArg) | |
1071 { | |
1072 var body = func.Body.Unwrap(); | |
1073 var expr = body.Convert(ex => | |
1074 { | |
1075 if (ex == func.Parameters[0]) | |
1076 return _sourceExpression; | |
1077 | |
1078 if (ex == func.Parameters[1]) | |
1079 return _colSelector.Body.Convert(e => e == _colSelector.Parameters[0] ? colArg : e); | |
1080 | |
1081 return ex; | |
1082 }); | |
1083 | |
1084 return expr; | |
1085 } | |
1086 } | |
1087 | |
1088 Expression ConvertSelectMany(MethodCallExpression method) | |
1089 { | |
1090 if (method.Arguments.Count != 2 || ((LambdaExpression)method.Arguments[1].Unwrap()).Parameters.Count != 1) | |
1091 return method; | |
1092 | |
1093 var types = method.Method.GetGenericMethodDefinition().GetGenericArguments() | |
1094 .Zip(method.Method.GetGenericArguments(), (n, t) => new { n = n.Name, t }) | |
1095 .ToDictionary(_ => _.n, _ => _.t); | |
1096 | |
1097 var sourceExpression = OptimizeExpression(method.Arguments[0].Unwrap()); | |
1098 var colSelector = (LambdaExpression)OptimizeExpression(method.Arguments[1].Unwrap()); | |
1099 | |
1100 var gtype = typeof(SelectManyHelper<,>).MakeGenericType(types["TSource"], types["TResult"]); | |
1101 var helper = | |
1102 //Expression.Lambda<Func<ISelectManyHelper>>( | |
1103 // Expression.Convert(Expression.New(gtype), typeof(ISelectManyHelper))) | |
1104 //.Compile()(); | |
1105 (ISelectManyHelper)Activator.CreateInstance(gtype); | |
1106 | |
1107 helper.Set(sourceExpression, colSelector); | |
1108 | |
1109 return method.Method.DeclaringType == typeof(Queryable) ? | |
1110 helper.AddElementSelectorQ() : | |
1111 helper.AddElementSelectorE(); | |
1112 } | |
1113 | |
1114 #endregion | |
1115 | |
1116 #region ConvertPredicate | |
1117 | |
1118 Expression ConvertPredicate(MethodCallExpression method) | |
1119 { | |
1120 if (method.Arguments.Count != 2) | |
1121 return method; | |
1122 | |
1123 var cm = GetQueriableMethodInfo(method, (m,_) => m.Name == method.Method.Name && m.GetParameters().Length == 1); | |
1124 var wm = GetMethodInfo(method, "Where"); | |
1125 | |
1126 var argType = method.Method.GetGenericArguments()[0]; | |
1127 | |
1128 wm = wm.MakeGenericMethod(argType); | |
1129 cm = cm.MakeGenericMethod(argType); | |
1130 | |
1131 return Expression.Call(null, cm, | |
1132 Expression.Call(null, wm, | |
1133 OptimizeExpression(method.Arguments[0]), | |
1134 OptimizeExpression(method.Arguments[1]))); | |
1135 } | |
1136 | |
1137 #endregion | |
1138 | |
1139 #region ConvertSelector | |
1140 | |
1141 Expression ConvertSelector(MethodCallExpression method, bool isGeneric) | |
1142 { | |
1143 if (method.Arguments.Count != 2) | |
1144 return method; | |
1145 | |
1146 isGeneric = isGeneric && method.Method.DeclaringType == typeof(Queryable); | |
1147 | |
1148 var types = GetMethodGenericTypes(method); | |
1149 var sm = GetMethodInfo(method, "Select"); | |
1150 var cm = GetQueriableMethodInfo(method, (m,isDefault) => | |
1151 { | |
1152 if (m.Name == method.Method.Name) | |
1153 { | |
1154 var ps = m.GetParameters(); | |
1155 | |
1156 if (ps.Length == 1) | |
1157 { | |
1158 if (isGeneric) | |
1159 return true; | |
1160 | |
1161 var ts = ps[0].ParameterType.GetGenericArguments(); | |
1162 return ts[0] == types[1] || isDefault && ts[0].IsGenericParameter; | |
1163 } | |
1164 } | |
1165 | |
1166 return false; | |
1167 }); | |
1168 | |
1169 var argType = types[0]; | |
1170 | |
1171 sm = sm.MakeGenericMethod(argType, types[1]); | |
1172 | |
1173 if (cm.IsGenericMethodDefinition) | |
1174 cm = cm.MakeGenericMethod(types[1]); | |
1175 | |
1176 return Expression.Call(null, cm, | |
1177 OptimizeExpression(Expression.Call(null, sm, | |
1178 method.Arguments[0], | |
1179 method.Arguments[1]))); | |
1180 } | |
1181 | |
1182 #endregion | |
1183 | |
1184 #region ConvertSelect | |
1185 | |
1186 Expression ConvertSelect(MethodCallExpression method) | |
1187 { | |
1188 var sequence = OptimizeExpression(method.Arguments[0]); | |
1189 var lambda1 = (LambdaExpression)method.Arguments[1].Unwrap(); | |
1190 var lambda = (LambdaExpression)OptimizeExpression(lambda1); | |
1191 | |
1192 if (lambda1.Parameters.Count > 1 || | |
1193 sequence.NodeType != ExpressionType.Call || | |
1194 ((MethodCallExpression)sequence).Method.Name != method.Method.Name) | |
1195 { | |
1196 return method; | |
1197 } | |
1198 | |
1199 var slambda = (LambdaExpression)((MethodCallExpression)sequence).Arguments[1].Unwrap(); | |
1200 var sbody = slambda.Body.Unwrap(); | |
1201 | |
1202 if (slambda.Parameters.Count > 1 || sbody.NodeType != ExpressionType.MemberAccess) | |
1203 return method; | |
1204 | |
1205 var types1 = GetMethodGenericTypes((MethodCallExpression)sequence); | |
1206 var types2 = GetMethodGenericTypes(method); | |
1207 | |
1208 var expr = Expression.Call(null, | |
1209 GetMethodInfo(method, "Select").MakeGenericMethod(types1[0], types2[1]), | |
1210 ((MethodCallExpression)sequence).Arguments[0], | |
1211 Expression.Lambda( | |
1212 lambda.Body.Convert(ex => ex == lambda.Parameters[0] ? sbody : ex), | |
1213 slambda.Parameters[0])); | |
1214 | |
1215 return expr; | |
1216 } | |
1217 | |
1218 #endregion | |
1219 | |
1220 #region ConvertIQueriable | |
1221 | |
1222 Expression ConvertIQueriable(Expression expression) | |
1223 { | |
1224 if (expression.NodeType == ExpressionType.MemberAccess || expression.NodeType == ExpressionType.Call) | |
1225 { | |
1226 var p = Expression.Parameter(typeof(Expression), "exp"); | |
1227 var exas = expression.GetExpressionAccessors(p); | |
1228 var expr = ReplaceParameter(exas, expression, _ => {}); | |
1229 | |
1230 if (expr.Find(e => e.NodeType == ExpressionType.Parameter && e != p) != null) | |
1231 return expression; | |
1232 | |
1233 var l = Expression.Lambda<Func<Expression,IQueryable>>(Expression.Convert(expr, typeof(IQueryable)), new [] { p }); | |
1234 var n = _query.AddQueryableAccessors(expression, l); | |
1235 | |
1236 Expression accessor; | |
1237 | |
1238 _expressionAccessors.TryGetValue(expression, out accessor); | |
1239 | |
1240 var path = | |
1241 Expression.Call( | |
1242 Expression.Constant(_query), | |
1243 ReflectionHelper.Expressor<Query>.MethodExpressor(a => a.GetIQueryable(0, null)), | |
1244 new[] { Expression.Constant(n), accessor ?? Expression.Constant(null, typeof(Expression)) }); | |
1245 | |
1246 var qex = _query.GetIQueryable(n, expression); | |
1247 | |
1248 if (expression.NodeType == ExpressionType.Call && qex.NodeType == ExpressionType.Call) | |
1249 { | |
1250 var m1 = (MethodCallExpression)expression; | |
1251 var m2 = (MethodCallExpression)qex; | |
1252 | |
1253 if (m1.Method == m2.Method) | |
1254 return expression; | |
1255 } | |
1256 | |
1257 foreach (var a in qex.GetExpressionAccessors(path)) | |
1258 if (!_expressionAccessors.ContainsKey(a.Key)) | |
1259 _expressionAccessors.Add(a.Key, a.Value); | |
1260 | |
1261 return qex; | |
1262 } | |
1263 | |
1264 throw new InvalidOperationException(); | |
1265 } | |
1266 | |
1267 #endregion | |
1268 | |
1269 #region ConvertElementAt | |
1270 | |
1271 Expression ConvertElementAt(MethodCallExpression method) | |
1272 { | |
1273 var sequence = OptimizeExpression(method.Arguments[0]); | |
1274 var index = OptimizeExpression(method.Arguments[1]).Unwrap(); | |
1275 var sourceType = method.Method.GetGenericArguments()[0]; | |
1276 | |
1277 MethodInfo skipMethod; | |
1278 | |
1279 if (index.NodeType == ExpressionType.Lambda) | |
1280 { | |
1281 skipMethod = ReflectionHelper.Expressor<object>.MethodExpressor(o => LinqExtensions.Skip<object>(null, null)); | |
1282 skipMethod = skipMethod.GetGenericMethodDefinition(); | |
1283 } | |
1284 else | |
1285 { | |
1286 skipMethod = GetQueriableMethodInfo(method, (mi,_) => mi.Name == "Skip"); | |
1287 } | |
1288 | |
1289 skipMethod = skipMethod.MakeGenericMethod(sourceType); | |
1290 | |
1291 var methodName = method.Method.Name == "ElementAt" ? "First" : "FirstOrDefault"; | |
1292 var firstMethod = GetQueriableMethodInfo(method, (mi,_) => mi.Name == methodName && mi.GetParameters().Length == 1); | |
1293 | |
1294 firstMethod = firstMethod.MakeGenericMethod(sourceType); | |
1295 | |
1296 return Expression.Call(null, firstMethod, Expression.Call(null, skipMethod, sequence, index)); | |
1297 } | |
1298 | |
1299 #endregion | |
1300 | |
1301 #region Helpers | |
1302 | |
1303 MethodInfo GetQueriableMethodInfo(MethodCallExpression method, Func<MethodInfo,bool,bool> predicate) | |
1304 { | |
1305 return method.Method.DeclaringType == typeof(Enumerable) ? | |
1306 EnumerableMethods.FirstOrDefault(m => predicate(m, false)) ?? EnumerableMethods.First(m => predicate(m, true)): | |
1307 QueryableMethods. FirstOrDefault(m => predicate(m, false)) ?? QueryableMethods. First(m => predicate(m, true)); | |
1308 } | |
1309 | |
1310 MethodInfo GetMethodInfo(MethodCallExpression method, string name) | |
1311 { | |
1312 return method.Method.DeclaringType == typeof(Enumerable) ? | |
1313 EnumerableMethods | |
1314 .Where(m => m.Name == name && m.GetParameters().Length == 2) | |
1315 .First(m => m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2) : | |
1316 QueryableMethods | |
1317 .Where(m => m.Name == name && m.GetParameters().Length == 2) | |
1318 .First(m => m.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2); | |
1319 } | |
1320 | |
1321 static Type[] GetMethodGenericTypes(MethodCallExpression method) | |
1322 { | |
1323 return method.Method.DeclaringType == typeof(Enumerable) ? | |
1324 method.Method.GetParameters()[1].ParameterType.GetGenericArguments() : | |
1325 method.Method.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments(); | |
1326 } | |
1327 | |
1328 #endregion | |
1329 | |
1330 #endregion | |
1331 } | |
1332 } |