comparison Source/Data/Linq/Builder/ExpressionBuilder.SqlBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children 1ef98bd70424
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Linq.Expressions;
6 using System.Reflection;
7 using System.Text;
8
9 namespace BLToolkit.Data.Linq.Builder
10 {
11 using BLToolkit.Linq;
12 using Common;
13 using Data.Sql;
14 using Mapping;
15 using Reflection;
16
17 partial class ExpressionBuilder
18 {
19 #region Build Where
20
21 public IBuildContext BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, bool checkForSubQuery)
22 {
23 var makeHaving = false;
24 var prevParent = sequence.Parent;
25
26 var ctx = new ExpressionContext(parent, sequence, condition);
27 var expr = ConvertExpression(condition.Body.Unwrap());
28
29 if (checkForSubQuery && CheckSubQueryForWhere(ctx, expr, out makeHaving))
30 {
31 ReplaceParent(ctx, prevParent);
32
33 sequence = new SubQueryContext(sequence);
34 prevParent = sequence.Parent;
35
36 ctx = new ExpressionContext(parent, sequence, condition);
37 }
38
39 BuildSearchCondition(
40 ctx,
41 expr,
42 makeHaving ?
43 ctx.SqlQuery.Having.SearchCondition.Conditions :
44 ctx.SqlQuery.Where. SearchCondition.Conditions);
45
46 ReplaceParent(ctx, prevParent);
47
48 return sequence;
49 }
50
51 bool CheckSubQueryForWhere(IBuildContext context, Expression expression, out bool makeHaving)
52 {
53 var makeSubQuery = false;
54 var isHaving = false;
55 var isWhere = false;
56
57 expression.Visit(expr =>
58 {
59 if (_subQueryExpressions != null && _subQueryExpressions.Contains(expr))
60 {
61 makeSubQuery = true;
62 isWhere = true;
63 return false;
64 }
65
66 var stopWalking = false;
67
68 switch (expr.NodeType)
69 {
70 case ExpressionType.MemberAccess:
71 {
72 var ma = (MemberExpression)expr;
73
74 if (TypeHelper.IsNullableValueMember(ma.Member) ||
75 TypeHelper.IsNullableHasValueMember(ma.Member))
76 break;
77
78 if (SqlProvider.ConvertMember(ma.Member) == null)
79 {
80 var ctx = GetContext(context, expr);
81
82 if (ctx != null)
83 {
84 if (ctx.IsExpression(expr, 0, RequestFor.Expression).Result)
85 makeSubQuery = true;
86 stopWalking = true;
87 }
88 }
89
90 isWhere = true;
91
92 break;
93 }
94
95 case ExpressionType.Call:
96 {
97 var e = (MethodCallExpression)expr;
98
99 if (e.Method.DeclaringType == typeof(Enumerable) && e.Method.Name != "Contains")
100 return isHaving = true;
101
102 isWhere = true;
103
104 break;
105 }
106
107 case ExpressionType.Parameter:
108 {
109 var ctx = GetContext(context, expr);
110
111 if (ctx != null)
112 {
113 if (ctx.IsExpression(expr, 0, RequestFor.Expression).Result)
114 makeSubQuery = true;
115 stopWalking = true;
116 }
117
118 isWhere = true;
119
120 break;
121 }
122 }
123
124 return !stopWalking;
125 });
126
127 makeHaving = isHaving && !isWhere;
128 return makeSubQuery || isHaving && isWhere;
129 }
130
131 #endregion
132
133 #region BuildTake
134
135 public void BuildTake(IBuildContext context, ISqlExpression expr)
136 {
137 var sql = context.SqlQuery;
138
139 sql.Select.Take(expr);
140
141 SqlProvider.SqlQuery = sql;
142
143 if (sql.Select.SkipValue != null && SqlProvider.IsTakeSupported && !SqlProvider.IsSkipSupported)
144 {
145 if (context.SqlQuery.Select.SkipValue is SqlParameter && sql.Select.TakeValue is SqlValue)
146 {
147 var skip = (SqlParameter)sql.Select.SkipValue;
148 var parm = (SqlParameter)sql.Select.SkipValue.Clone(new Dictionary<ICloneableElement,ICloneableElement>(), _ => true);
149
150 parm.SetTakeConverter((int)((SqlValue)sql.Select.TakeValue).Value);
151
152 sql.Select.Take(parm);
153
154 var ep = (from pm in CurrentSqlParameters where pm.SqlParameter == skip select pm).First();
155
156 ep = new ParameterAccessor
157 {
158 Expression = ep.Expression,
159 Accessor = ep.Accessor,
160 SqlParameter = parm
161 };
162
163 CurrentSqlParameters.Add(ep);
164 }
165 else
166 sql.Select.Take(Convert(
167 context,
168 new SqlBinaryExpression(typeof(int), sql.Select.SkipValue, "+", sql.Select.TakeValue, Precedence.Additive)));
169 }
170
171 if (!SqlProvider.TakeAcceptsParameter)
172 {
173 var p = sql.Select.TakeValue as SqlParameter;
174
175 if (p != null)
176 p.IsQueryParameter = false;
177 }
178 }
179
180 #endregion
181
182 #region SubQueryToSql
183
184 public IBuildContext GetSubQuery(IBuildContext context, MethodCallExpression expr)
185 {
186 var info = new BuildInfo(context, expr, new SqlQuery { ParentSql = context.SqlQuery });
187 var ctx = BuildSequence(info);
188
189 if (ctx.SqlQuery.Select.Columns.Count == 0 &&
190 (ctx.IsExpression(null, 0, RequestFor.Expression).Result ||
191 ctx.IsExpression(null, 0, RequestFor.Field). Result))
192 {
193 ctx.ConvertToIndex(null, 0, ConvertFlags.Field);
194 }
195
196 return ctx;
197 }
198
199 internal ISqlExpression SubQueryToSql(IBuildContext context, MethodCallExpression expression)
200 {
201 var sequence = GetSubQuery(context, expression);
202 var subSql = sequence.GetSubQuery(context);
203
204 if (subSql != null)
205 return subSql;
206
207 var query = context.SqlQuery;
208 var subQuery = sequence.SqlQuery;
209
210 // This code should be moved to context.
211 //
212 if (!query.GroupBy.IsEmpty && !subQuery.Where.IsEmpty)
213 {
214 var fromGroupBy = sequence.SqlQuery.Properties
215 .OfType<System.Tuple<string,SqlQuery>>()
216 .Where(p => p.Item1 == "from_group_by" && p.Item2 == context.SqlQuery)
217 .Any();
218
219 if (fromGroupBy)
220 {
221 if (subQuery.Select.Columns.Count == 1 &&
222 subQuery.Select.Columns[0].Expression.ElementType == QueryElementType.SqlFunction &&
223 subQuery.GroupBy.IsEmpty && !subQuery.Select.HasModifier && !subQuery.HasUnion &&
224 subQuery.Where.SearchCondition.Conditions.Count == 1)
225 {
226 var cond = subQuery.Where.SearchCondition.Conditions[0];
227
228 if (cond.Predicate.ElementType == QueryElementType.ExprExprPredicate && query.GroupBy.Items.Count == 1 ||
229 cond.Predicate.ElementType == QueryElementType.SearchCondition &&
230 query.GroupBy.Items.Count == ((SqlQuery.SearchCondition)cond.Predicate).Conditions.Count)
231 {
232 var func = (SqlFunction)subQuery.Select.Columns[0].Expression;
233
234 if (CountBuilder.MethodNames.Contains(func.Name))
235 return SqlFunction.CreateCount(func.SystemType, query);
236 }
237 }
238 }
239 }
240
241 return sequence.SqlQuery;
242 }
243
244 #endregion
245
246 #region IsSubQuery
247
248 bool IsSubQuery(IBuildContext context, MethodCallExpression call)
249 {
250 if (call.IsQueryable())
251 {
252 var info = new BuildInfo(context, call, new SqlQuery { ParentSql = context.SqlQuery });
253
254 if (!IsSequence(info))
255 return false;
256
257 var arg = call.Arguments[0];
258
259 if (AggregationBuilder.MethodNames.Contains(call.Method.Name))
260 while (arg.NodeType == ExpressionType.Call && ((MethodCallExpression) arg).Method.Name == "Select")
261 arg = ((MethodCallExpression)arg).Arguments[0];
262
263 var mc = arg as MethodCallExpression;
264
265 while (mc != null)
266 {
267 if (!mc.IsQueryable())
268 return GetTableFunctionAttribute(mc.Method) != null;
269
270 mc = mc.Arguments[0] as MethodCallExpression;
271 }
272
273 return arg.NodeType == ExpressionType.Call || IsSubQuerySource(context, arg);
274 }
275
276 return false;
277 }
278
279 bool IsSubQuerySource(IBuildContext context, Expression expr)
280 {
281 if (expr == null)
282 return false;
283
284 var ctx = GetContext(context, expr);
285
286 if (ctx != null && ctx.IsExpression(expr, 0, RequestFor.Object).Result)
287 return true;
288
289 while (expr != null && expr.NodeType == ExpressionType.MemberAccess)
290 expr = ((MemberExpression)expr).Expression;
291
292 return expr != null && expr.NodeType == ExpressionType.Constant;
293 }
294
295 bool IsGroupJoinSource(IBuildContext context, MethodCallExpression call)
296 {
297 if (!call.IsQueryable() || CountBuilder.MethodNames.Contains(call.Method.Name))
298 return false;
299
300 Expression expr = call;
301
302 while (expr.NodeType == ExpressionType.Call)
303 expr = ((MethodCallExpression)expr).Arguments[0];
304
305 var ctx = GetContext(context, expr);
306
307 return ctx != null && ctx.IsExpression(expr, 0, RequestFor.GroupJoin).Result;
308 }
309
310 #endregion
311
312 #region ConvertExpression
313
314 interface IConvertHelper
315 {
316 Expression ConvertNull(MemberExpression expression);
317 }
318
319 class ConvertHelper<T> : IConvertHelper
320 where T : struct
321 {
322 public Expression ConvertNull(MemberExpression expression)
323 {
324 return Expression.Call(
325 null,
326 ReflectionHelper.Expressor<T?>.MethodExpressor(p => Sql.ConvertNullable(p)),
327 expression.Expression);
328 }
329 }
330
331 Expression ConvertExpression(Expression expression)
332 {
333 return expression.Convert2(e =>
334 {
335 if (CanBeConstant(e) || CanBeCompiled(e))
336 return new ExpressionHelper.ConvertInfo(e, true);
337
338 switch (e.NodeType)
339 {
340 case ExpressionType.New:
341 {
342 var ex = ConvertNew((NewExpression)e);
343 if (ex != null)
344 return new ExpressionHelper.ConvertInfo(ConvertExpression(ex));
345 break;
346 }
347
348 case ExpressionType.Call:
349 {
350 var cm = ConvertMethod((MethodCallExpression)e);
351 if (cm != null)
352 return new ExpressionHelper.ConvertInfo(ConvertExpression(cm));
353 break;
354 }
355
356 case ExpressionType.MemberAccess:
357 {
358 var ma = (MemberExpression)e;
359 var l = SqlProvider.ConvertMember(ma.Member);
360
361 if (l != null)
362 {
363 var body = l.Body.Unwrap();
364 var expr = body.Convert(wpi => wpi.NodeType == ExpressionType.Parameter ? ma.Expression : wpi);
365
366 if (expr.Type != e.Type)
367 expr = new ChangeTypeExpression(expr, e.Type);
368
369 return new ExpressionHelper.ConvertInfo(ConvertExpression(expr));
370 }
371
372 if (TypeHelper.IsNullableValueMember(ma.Member))
373 {
374 var ntype = typeof(ConvertHelper<>).MakeGenericType(ma.Type);
375 var helper = (IConvertHelper)Activator.CreateInstance(ntype);
376 var expr = helper.ConvertNull(ma);
377
378 return new ExpressionHelper.ConvertInfo(ConvertExpression(expr));
379 }
380
381 if (ma.Member.DeclaringType == typeof(TimeSpan))
382 {
383 switch (ma.Expression.NodeType)
384 {
385 case ExpressionType.Subtract :
386 case ExpressionType.SubtractChecked:
387
388 Sql.DateParts datePart;
389
390 switch (ma.Member.Name)
391 {
392 case "TotalMilliseconds" : datePart = Sql.DateParts.Millisecond; break;
393 case "TotalSeconds" : datePart = Sql.DateParts.Second; break;
394 case "TotalMinutes" : datePart = Sql.DateParts.Minute; break;
395 case "TotalHours" : datePart = Sql.DateParts.Hour; break;
396 case "TotalDays" : datePart = Sql.DateParts.Day; break;
397 default : return new ExpressionHelper.ConvertInfo(e);
398 }
399
400 var ex = (BinaryExpression)ma.Expression;
401 var method = ReflectionHelper.Expressor<object>.MethodExpressor(
402 _ => Sql.DateDiff(Sql.DateParts.Day, DateTime.MinValue, DateTime.MinValue));
403
404 var call =
405 Expression.Convert(
406 Expression.Call(
407 null,
408 method,
409 Expression.Constant(datePart),
410 Expression.Convert(ex.Right, typeof(DateTime?)),
411 Expression.Convert(ex.Left, typeof(DateTime?))),
412 typeof(double));
413
414 return new ExpressionHelper.ConvertInfo(ConvertExpression(call));
415 }
416 }
417
418 break;
419 }
420 }
421
422 return new ExpressionHelper.ConvertInfo(e);
423 });
424 }
425
426 Expression ConvertMethod(MethodCallExpression pi)
427 {
428 var l = SqlProvider.ConvertMember(pi.Method);
429 return l == null ? null : ConvertMethod(pi, l);
430 }
431
432 static Expression ConvertMethod(MethodCallExpression pi, LambdaExpression lambda)
433 {
434 var ef = lambda.Body.Unwrap();
435 var parms = new Dictionary<string,int>(lambda.Parameters.Count);
436 var pn = pi.Method.IsStatic ? 0 : -1;
437
438 foreach (var p in lambda.Parameters)
439 parms.Add(p.Name, pn++);
440
441 var pie = ef.Convert(wpi =>
442 {
443 if (wpi.NodeType == ExpressionType.Parameter)
444 {
445 int n;
446 if (parms.TryGetValue(((ParameterExpression)wpi).Name, out n))
447 return n < 0 ? pi.Object : pi.Arguments[n];
448 }
449
450 return wpi;
451 });
452
453 if (pi.Method.ReturnType != pie.Type)
454 pie = new ChangeTypeExpression(pie, pi.Method.ReturnType);
455
456 return pie;
457 }
458
459 Expression ConvertNew(NewExpression pi)
460 {
461 var lambda = SqlProvider.ConvertMember(pi.Constructor);
462
463 if (lambda != null)
464 {
465 var ef = lambda.Body.Unwrap();
466 var parms = new Dictionary<string,int>(lambda.Parameters.Count);
467 var pn = 0;
468
469 foreach (var p in lambda.Parameters)
470 parms.Add(p.Name, pn++);
471
472 return ef.Convert(wpi =>
473 {
474 if (wpi.NodeType == ExpressionType.Parameter)
475 {
476 var pe = (ParameterExpression)wpi;
477 var n = parms[pe.Name];
478 return pi.Arguments[n];
479 }
480
481 return wpi;
482 });
483 }
484
485 return null;
486 }
487
488 #endregion
489
490 #region BuildExpression
491
492 public SqlInfo[] ConvertExpressions(IBuildContext context, Expression expression, ConvertFlags queryConvertFlag)
493 {
494 expression = ConvertExpression(expression);
495
496 switch (expression.NodeType)
497 {
498 case ExpressionType.New :
499 {
500 var expr = (NewExpression)expression;
501
502 // ReSharper disable ConditionIsAlwaysTrueOrFalse
503 // ReSharper disable HeuristicUnreachableCode
504 if (expr.Members == null)
505 return Array<SqlInfo>.Empty;
506 // ReSharper restore HeuristicUnreachableCode
507 // ReSharper restore ConditionIsAlwaysTrueOrFalse
508
509 return expr.Arguments
510 .Select((arg,i) =>
511 {
512 var mi = expr.Members[i];
513 if (mi is MethodInfo)
514 mi = TypeHelper.GetPropertyByMethod((MethodInfo)mi);
515
516 return ConvertExpressions(context, arg, queryConvertFlag).Select(si => si.Clone(mi));
517 })
518 .SelectMany(si => si)
519 .ToArray();
520 }
521
522 case ExpressionType.MemberInit :
523 {
524 var expr = (MemberInitExpression)expression;
525 var dic = TypeAccessor.GetAccessor(expr.Type)
526 .Select((m,i) => new { m, i })
527 .ToDictionary(_ => _.m.MemberInfo, _ => _.i);
528
529 return expr.Bindings
530 .Where (b => b is MemberAssignment)
531 .Cast<MemberAssignment>()
532 .OrderBy(b => dic[b.Member])
533 .Select (a =>
534 {
535 var mi = a.Member;
536 if (mi is MethodInfo)
537 mi = TypeHelper.GetPropertyByMethod((MethodInfo)mi);
538
539 return ConvertExpressions(context, a.Expression, queryConvertFlag).Select(si => si.Clone(mi));
540 })
541 .SelectMany(si => si)
542 .ToArray();
543 }
544 }
545
546 var ctx = GetContext(context, expression);
547
548 if (ctx != null && ctx.IsExpression(expression, 0, RequestFor.Object).Result)
549 return ctx.ConvertToSql(expression, 0, queryConvertFlag);
550
551 return new[] { new SqlInfo { Sql = ConvertToSql(context, expression, false) } };
552 }
553
554 public ISqlExpression ConvertToSqlExpression(IBuildContext context, Expression expression, bool convertEnum)
555 {
556 var expr = ConvertExpression(expression);
557 return ConvertToSql(context, expr, false, convertEnum);
558 }
559
560 #if FW3
561 public ISqlExpression ConvertToSql(IBuildContext context, Expression expression, bool unwrap)
562 {
563 return ConvertToSql(context, expression, unwrap, true);
564 }
565 #endif
566
567 public ISqlExpression ConvertToSql(IBuildContext context, Expression expression, bool unwrap, bool convertEnum
568 #if !FW3
569 = true
570 #endif
571 )
572 {
573 if (CanBeConstant(expression))
574 return BuildConstant(expression, convertEnum);
575
576 if (CanBeCompiled(expression))
577 return BuildParameter(expression).SqlParameter;
578
579 if (unwrap)
580 expression = expression.Unwrap();
581
582 switch (expression.NodeType)
583 {
584 case ExpressionType.AndAlso :
585 case ExpressionType.OrElse :
586 case ExpressionType.Not :
587 case ExpressionType.Equal :
588 case ExpressionType.NotEqual :
589 case ExpressionType.GreaterThan :
590 case ExpressionType.GreaterThanOrEqual :
591 case ExpressionType.LessThan :
592 case ExpressionType.LessThanOrEqual :
593 {
594 var condition = new SqlQuery.SearchCondition();
595 BuildSearchCondition(context, expression, condition.Conditions);
596 return condition;
597 }
598
599 case ExpressionType.And :
600 case ExpressionType.Or :
601 {
602 if (expression.Type == typeof(bool))
603 goto case ExpressionType.AndAlso;
604 goto case ExpressionType.Add;
605 }
606
607 case ExpressionType.Add :
608 case ExpressionType.AddChecked :
609 case ExpressionType.Divide :
610 case ExpressionType.ExclusiveOr :
611 case ExpressionType.Modulo :
612 case ExpressionType.Multiply :
613 case ExpressionType.MultiplyChecked :
614 case ExpressionType.Power :
615 case ExpressionType.Subtract :
616 case ExpressionType.SubtractChecked :
617 case ExpressionType.Coalesce :
618 {
619 var e = (BinaryExpression)expression;
620 var l = ConvertToSql(context, e.Left, false);
621 var r = ConvertToSql(context, e.Right, false);
622 var t = e.Type;
623
624 switch (expression.NodeType)
625 {
626 case ExpressionType.Add :
627 case ExpressionType.AddChecked : return Convert(context, new SqlBinaryExpression(t, l, "+", r, Precedence.Additive));
628 case ExpressionType.And : return Convert(context, new SqlBinaryExpression(t, l, "&", r, Precedence.Bitwise));
629 case ExpressionType.Divide : return Convert(context, new SqlBinaryExpression(t, l, "/", r, Precedence.Multiplicative));
630 case ExpressionType.ExclusiveOr : return Convert(context, new SqlBinaryExpression(t, l, "^", r, Precedence.Bitwise));
631 case ExpressionType.Modulo : return Convert(context, new SqlBinaryExpression(t, l, "%", r, Precedence.Multiplicative));
632 case ExpressionType.Multiply:
633 case ExpressionType.MultiplyChecked : return Convert(context, new SqlBinaryExpression(t, l, "*", r, Precedence.Multiplicative));
634 case ExpressionType.Or : return Convert(context, new SqlBinaryExpression(t, l, "|", r, Precedence.Bitwise));
635 case ExpressionType.Power : return Convert(context, new SqlFunction(t, "Power", l, r));
636 case ExpressionType.Subtract :
637 case ExpressionType.SubtractChecked : return Convert(context, new SqlBinaryExpression(t, l, "-", r, Precedence.Subtraction));
638 case ExpressionType.Coalesce :
639 {
640 if (r is SqlFunction)
641 {
642 var c = (SqlFunction)r;
643
644 if (c.Name == "Coalesce")
645 {
646 var parms = new ISqlExpression[c.Parameters.Length + 1];
647
648 parms[0] = l;
649 c.Parameters.CopyTo(parms, 1);
650
651 return Convert(context, new SqlFunction(t, "Coalesce", parms));
652 }
653 }
654
655 return Convert(context, new SqlFunction(t, "Coalesce", l, r));
656 }
657 }
658
659 break;
660 }
661
662 case ExpressionType.UnaryPlus :
663 case ExpressionType.Negate :
664 case ExpressionType.NegateChecked :
665 {
666 var e = (UnaryExpression)expression;
667 var o = ConvertToSql(context, e.Operand, false);
668 var t = e.Type;
669
670 switch (expression.NodeType)
671 {
672 case ExpressionType.UnaryPlus : return o;
673 case ExpressionType.Negate :
674 case ExpressionType.NegateChecked :
675 return Convert(context, new SqlBinaryExpression(t, new SqlValue(-1), "*", o, Precedence.Multiplicative));
676 }
677
678 break;
679 }
680
681 case ExpressionType.Convert :
682 case ExpressionType.ConvertChecked :
683 {
684 var e = (UnaryExpression)expression;
685 var o = ConvertToSql(context, e.Operand, false);
686
687 if (e.Method == null && e.IsLifted)
688 return o;
689
690 var t = e.Operand.Type;
691 var s = SqlDataType.GetDataType(t);
692
693 if (o.SystemType != null && s.Type == typeof(object))
694 {
695 t = o.SystemType;
696 s = SqlDataType.GetDataType(t);
697 }
698
699 if (e.Type == t ||
700 t.IsEnum && Enum.GetUnderlyingType(t) == e.Type ||
701 e.Type.IsEnum && Enum.GetUnderlyingType(e.Type) == t)
702 return o;
703
704 return Convert(
705 context,
706 new SqlFunction(e.Type, "$Convert$", SqlDataType.GetDataType(e.Type), s, o));
707 }
708
709 case ExpressionType.Conditional :
710 {
711 var e = (ConditionalExpression)expression;
712 var s = ConvertToSql(context, e.Test, false);
713 var t = ConvertToSql(context, e.IfTrue, false);
714 var f = ConvertToSql(context, e.IfFalse, false);
715
716 if (f is SqlFunction)
717 {
718 var c = (SqlFunction)f;
719
720 if (c.Name == "CASE")
721 {
722 var parms = new ISqlExpression[c.Parameters.Length + 2];
723
724 parms[0] = s;
725 parms[1] = t;
726 c.Parameters.CopyTo(parms, 2);
727
728 return Convert(context, new SqlFunction(e.Type, "CASE", parms));
729 }
730 }
731
732 return Convert(context, new SqlFunction(e.Type, "CASE", s, t, f));
733 }
734
735 case ExpressionType.MemberAccess :
736 {
737 var ma = (MemberExpression)expression;
738 var attr = GetFunctionAttribute(ma.Member);
739
740 if (attr != null)
741 return Convert(context, attr.GetExpression(ma.Member));
742
743 var ctx = GetContext(context, expression);
744
745 if (ctx != null)
746 {
747 var sql = ctx.ConvertToSql(expression, 0, ConvertFlags.Field);
748
749 switch (sql.Length)
750 {
751 case 0 : break;
752 case 1 : return sql[0].Sql;
753 default : throw new InvalidOperationException();
754 }
755 }
756
757 break;
758 }
759
760 case ExpressionType.Parameter :
761 {
762 var ctx = GetContext(context, expression);
763
764 if (ctx != null)
765 {
766 var sql = ctx.ConvertToSql(expression, 0, ConvertFlags.Field);
767
768 switch (sql.Length)
769 {
770 case 0 : break;
771 case 1 : return sql[0].Sql;
772 default : throw new InvalidOperationException();
773 }
774 }
775
776 break;
777 }
778
779 case ExpressionType.Call :
780 {
781 var e = (MethodCallExpression)expression;
782
783 if (e.IsQueryable())
784 {
785 if (IsSubQuery(context, e))
786 return SubQueryToSql(context, e);
787
788 if (CountBuilder.MethodNames.Concat(AggregationBuilder.MethodNames).Contains(e.Method.Name))
789 {
790 var ctx = GetContext(context, expression);
791
792 if (ctx != null)
793 {
794 var sql = ctx.ConvertToSql(expression, 0, ConvertFlags.Field);
795
796 if (sql.Length != 1)
797 throw new InvalidOperationException();
798
799 return sql[0].Sql;
800 }
801
802 break;
803 }
804
805 return SubQueryToSql(context, e);
806 }
807
808 var attr = GetFunctionAttribute(e.Method);
809
810 if (attr != null)
811 {
812 var parms = new List<ISqlExpression>();
813
814 if (e.Object != null)
815 parms.Add(ConvertToSql(context, e.Object, false));
816
817 parms.AddRange(e.Arguments.Select(t => ConvertToSql(context, t, false)));
818
819 return Convert(context, attr.GetExpression(e.Method, parms.ToArray()));
820 }
821
822 break;
823 }
824
825 case ExpressionType.Invoke :
826 {
827 var pi = (InvocationExpression)expression;
828 var ex = pi.Expression;
829
830 if (ex.NodeType == ExpressionType.Quote)
831 ex = ((UnaryExpression)ex).Operand;
832
833 if (ex.NodeType == ExpressionType.Lambda)
834 {
835 var l = (LambdaExpression)ex;
836 var dic = new Dictionary<Expression,Expression>();
837
838 for (var i = 0; i < l.Parameters.Count; i++)
839 dic.Add(l.Parameters[i], pi.Arguments[i]);
840
841 var pie = l.Body.Convert(wpi =>
842 {
843 Expression ppi;
844 return dic.TryGetValue(wpi, out ppi) ? ppi : wpi;
845 });
846
847 return ConvertToSql(context, pie, false);
848 }
849
850 break;
851 }
852
853 case ExpressionType.TypeIs :
854 {
855 var condition = new SqlQuery.SearchCondition();
856 BuildSearchCondition(context, expression, condition.Conditions);
857 return condition;
858 }
859
860 case (ExpressionType)ChangeTypeExpression.ChangeTypeType :
861 return ConvertToSql(context, ((ChangeTypeExpression)expression).Expression, false);
862 }
863
864 if (expression.Type == typeof(bool) && _convertedPredicates.Add(expression))
865 {
866 var predicate = ConvertPredicate(context, expression);
867 _convertedPredicates.Remove(expression);
868 if (predicate != null)
869 return new SqlQuery.SearchCondition(new SqlQuery.Condition(false, predicate));
870 }
871
872 throw new LinqException("'{0}' cannot be converted to SQL.", expression);
873 }
874
875 readonly HashSet<Expression> _convertedPredicates = new HashSet<Expression>();
876
877 #endregion
878
879 #region IsServerSideOnly
880
881 bool IsServerSideOnly(Expression expr)
882 {
883 switch (expr.NodeType)
884 {
885 case ExpressionType.MemberAccess:
886 {
887 var ex = (MemberExpression)expr;
888 var l = SqlProvider.ConvertMember(ex.Member);
889
890 if (l != null)
891 return IsServerSideOnly(l.Body.Unwrap());
892
893 var attr = GetFunctionAttribute(ex.Member);
894 return attr != null && attr.ServerSideOnly;
895 }
896
897 case ExpressionType.Call:
898 {
899 var e = (MethodCallExpression)expr;
900
901 if (e.Method.DeclaringType == typeof(Enumerable))
902 {
903 if (CountBuilder.MethodNames.Concat(AggregationBuilder.MethodNames).Contains(e.Method.Name))
904 return IsQueryMember(e.Arguments[0]);
905 }
906 else if (e.Method.DeclaringType == typeof(Queryable))
907 {
908 switch (e.Method.Name)
909 {
910 case "Any" :
911 case "All" :
912 case "Contains" : return true;
913 }
914 }
915 else
916 {
917 var l = SqlProvider.ConvertMember(e.Method);
918
919 if (l != null)
920 return l.Body.Unwrap().Find(IsServerSideOnly) != null;
921
922 var attr = GetFunctionAttribute(e.Method);
923 return attr != null && attr.ServerSideOnly;
924 }
925
926 break;
927 }
928 }
929
930 return false;
931 }
932
933 static bool IsQueryMember(Expression expr)
934 {
935 if (expr != null) switch (expr.NodeType)
936 {
937 case ExpressionType.Parameter : return true;
938 case ExpressionType.MemberAccess : return IsQueryMember(((MemberExpression)expr).Expression);
939 case ExpressionType.Call :
940 {
941 var call = (MethodCallExpression)expr;
942
943 if (call.Method.DeclaringType == typeof(Queryable))
944 return true;
945
946 if (call.Method.DeclaringType == typeof(Enumerable) && call.Arguments.Count > 0)
947 return IsQueryMember(call.Arguments[0]);
948
949 return IsQueryMember(call.Object);
950 }
951 }
952
953 return false;
954 }
955
956 #endregion
957
958 #region CanBeConstant
959
960 bool CanBeConstant(Expression expr)
961 {
962 return null == expr.Find(ex =>
963 {
964 if (ex is BinaryExpression || ex is UnaryExpression /*|| ex.NodeType == ExpressionType.Convert*/)
965 return false;
966
967 switch (ex.NodeType)
968 {
969 case ExpressionType.Constant :
970 {
971 var c = (ConstantExpression)ex;
972
973 if (c.Value == null || ExpressionHelper.IsConstant(ex.Type))
974 return false;
975
976 break;
977 }
978
979 case ExpressionType.MemberAccess :
980 {
981 var ma = (MemberExpression)ex;
982
983 if (ExpressionHelper.IsConstant(ma.Member.DeclaringType) || TypeHelper.IsNullableValueMember(ma.Member))
984 return false;
985
986 break;
987 }
988
989 case ExpressionType.Call :
990 {
991 var mc = (MethodCallExpression)ex;
992
993 if (ExpressionHelper.IsConstant(mc.Method.DeclaringType) || mc.Method.DeclaringType == typeof(object))
994 return false;
995
996 var attr = GetFunctionAttribute(mc.Method);
997
998 if (attr != null && !attr.ServerSideOnly)
999 return false;
1000
1001 break;
1002 }
1003 }
1004
1005 return true;
1006 });
1007 }
1008
1009 #endregion
1010
1011 #region CanBeCompiled
1012
1013 bool CanBeCompiled(Expression expr)
1014 {
1015 return null == expr.Find(ex =>
1016 {
1017 if (IsServerSideOnly(ex))
1018 return true;
1019
1020 switch (ex.NodeType)
1021 {
1022 case ExpressionType.Parameter :
1023 return ex != ParametersParam;
1024
1025 case ExpressionType.MemberAccess :
1026 {
1027 var attr = GetFunctionAttribute(((MemberExpression)ex).Member);
1028 return attr != null && attr.ServerSideOnly;
1029 }
1030
1031 case ExpressionType.Call :
1032 {
1033 var attr = GetFunctionAttribute(((MethodCallExpression)ex).Method);
1034 return attr != null && attr.ServerSideOnly;
1035 }
1036 }
1037
1038 return false;
1039 });
1040 }
1041
1042 #endregion
1043
1044 #region Build Constant
1045
1046 readonly Dictionary<Expression,SqlValue> _constants = new Dictionary<Expression,SqlValue>();
1047
1048 SqlValue BuildConstant(Expression expr, bool convertEnum)
1049 {
1050 SqlValue value;
1051
1052 if (_constants.TryGetValue(expr, out value))
1053 return value;
1054
1055 var lambda = Expression.Lambda<Func<object>>(Expression.Convert(expr, typeof(object)));
1056 var v = lambda.Compile()();
1057
1058 if (v != null && convertEnum && v.GetType().IsEnum)
1059 {
1060 var attrs = v.GetType().GetCustomAttributes(typeof(SqlEnumAttribute), true);
1061
1062 v = Map.EnumToValue(v, attrs.Length == 0);
1063 }
1064
1065 value = new SqlValue(v);
1066
1067 _constants.Add(expr, value);
1068
1069 return value;
1070 }
1071
1072 #endregion
1073
1074 #region Build Parameter
1075
1076 readonly Dictionary<Expression,ParameterAccessor> _parameters = new Dictionary<Expression, ParameterAccessor>();
1077
1078 public readonly HashSet<Expression> AsParameters = new HashSet<Expression>();
1079
1080 ParameterAccessor BuildParameter(Expression expr)
1081 {
1082 ParameterAccessor p;
1083
1084 if (_parameters.TryGetValue(expr, out p))
1085 return p;
1086
1087 string name = null;
1088
1089 var newExpr = ReplaceParameter(_expressionAccessors, expr, nm => name = nm);
1090 var mapper = Expression.Lambda<Func<Expression,object[],object>>(
1091 Expression.Convert(newExpr, typeof(object)),
1092 new [] { ExpressionParam, ParametersParam });
1093
1094 p = new ParameterAccessor
1095 {
1096 Expression = expr,
1097 Accessor = mapper.Compile(),
1098 SqlParameter = new SqlParameter(expr.Type, name, null, MappingSchema)
1099 };
1100
1101 _parameters.Add(expr, p);
1102 CurrentSqlParameters.Add(p);
1103
1104 return p;
1105 }
1106
1107 Expression ReplaceParameter(IDictionary<Expression,Expression> expressionAccessors, Expression expression, Action<string> setName)
1108 {
1109 return expression.Convert(expr =>
1110 {
1111 if (expr.NodeType == ExpressionType.Constant)
1112 {
1113 var c = (ConstantExpression)expr;
1114
1115 if (!ExpressionHelper.IsConstant(expr.Type) || AsParameters.Contains(c))
1116 {
1117 Expression val;
1118
1119 if (expressionAccessors.TryGetValue(expr, out val))
1120 {
1121 expr = Expression.Convert(val, expr.Type);
1122
1123 if (expression.NodeType == ExpressionType.MemberAccess)
1124 {
1125 var ma = (MemberExpression)expression;
1126 setName(ma.Member.Name);
1127 }
1128 }
1129 }
1130 }
1131
1132 return expr;
1133 });
1134 }
1135
1136 #endregion
1137
1138 #region Predicate Converter
1139
1140 ISqlPredicate ConvertPredicate(IBuildContext context, Expression expression)
1141 {
1142 switch (expression.NodeType)
1143 {
1144 case ExpressionType.Equal :
1145 case ExpressionType.NotEqual :
1146 case ExpressionType.GreaterThan :
1147 case ExpressionType.GreaterThanOrEqual :
1148 case ExpressionType.LessThan :
1149 case ExpressionType.LessThanOrEqual :
1150 {
1151 var e = (BinaryExpression)expression;
1152 return ConvertCompare(context, expression.NodeType, e.Left, e.Right);
1153 }
1154
1155 case ExpressionType.Call :
1156 {
1157 var e = (MethodCallExpression)expression;
1158
1159 ISqlPredicate predicate = null;
1160
1161 if (e.Method.Name == "Equals" && e.Object != null && e.Arguments.Count == 1)
1162 return ConvertCompare(context, ExpressionType.Equal, e.Object, e.Arguments[0]);
1163
1164 if (e.Method.DeclaringType == typeof(string))
1165 {
1166 switch (e.Method.Name)
1167 {
1168 case "Contains" : predicate = ConvertLikePredicate(context, e, "%", "%"); break;
1169 case "StartsWith" : predicate = ConvertLikePredicate(context, e, "", "%"); break;
1170 case "EndsWith" : predicate = ConvertLikePredicate(context, e, "%", ""); break;
1171 }
1172 }
1173 else if (e.Method.Name == "Contains")
1174 {
1175 if (e.Method.DeclaringType == typeof(Enumerable) ||
1176 TypeHelper.IsSameOrParent(typeof(IList), e.Method.DeclaringType) ||
1177 TypeHelper.IsSameOrParent(typeof(ICollection<>), e.Method.DeclaringType))
1178 {
1179 predicate = ConvertInPredicate(context, e);
1180 }
1181 }
1182 else if (e.Method.Name == "ContainsValue" && TypeHelper.IsSameOrParent(typeof(Dictionary<,>), e.Method.DeclaringType))
1183 {
1184 var args = TypeHelper.GetGenericArguments(e.Method.DeclaringType, typeof(Dictionary<,>));
1185 var minf = EnumerableMethods
1186 .First(m => m.Name == "Contains" && m.GetParameters().Length == 2)
1187 .MakeGenericMethod(args[1]);
1188
1189 var expr = Expression.Call(
1190 minf,
1191 Expression.PropertyOrField(e.Object, "Values"),
1192 e.Arguments[0]);
1193
1194 predicate = ConvertInPredicate(context, expr);
1195 }
1196 else if (e.Method.Name == "ContainsKey" && TypeHelper.IsSameOrParent(typeof(IDictionary<,>), e.Method.DeclaringType))
1197 {
1198 var args = TypeHelper.GetGenericArguments(e.Method.DeclaringType, typeof(IDictionary<,>));
1199 var minf = EnumerableMethods
1200 .First(m => m.Name == "Contains" && m.GetParameters().Length == 2)
1201 .MakeGenericMethod(args[0]);
1202
1203 var expr = Expression.Call(
1204 minf,
1205 Expression.PropertyOrField(e.Object, "Keys"),
1206 e.Arguments[0]);
1207
1208 predicate = ConvertInPredicate(context, expr);
1209 }
1210 #if !SILVERLIGHT
1211 else if (e.Method == ReflectionHelper.Functions.String.Like11) predicate = ConvertLikePredicate(context, e);
1212 else if (e.Method == ReflectionHelper.Functions.String.Like12) predicate = ConvertLikePredicate(context, e);
1213 #endif
1214 else if (e.Method == ReflectionHelper.Functions.String.Like21) predicate = ConvertLikePredicate(context, e);
1215 else if (e.Method == ReflectionHelper.Functions.String.Like22) predicate = ConvertLikePredicate(context, e);
1216
1217 if (predicate != null)
1218 return Convert(context, predicate);
1219
1220 break;
1221 }
1222
1223 case ExpressionType.Conditional :
1224 return Convert(context,
1225 new SqlQuery.Predicate.ExprExpr(
1226 ConvertToSql(context, expression, false),
1227 SqlQuery.Predicate.Operator.Equal,
1228 new SqlValue(true)));
1229
1230 case ExpressionType.MemberAccess :
1231 {
1232 var e = (MemberExpression)expression;
1233
1234 if (e.Member.Name == "HasValue" &&
1235 e.Member.DeclaringType.IsGenericType &&
1236 e.Member.DeclaringType.GetGenericTypeDefinition() == typeof(Nullable<>))
1237 {
1238 var expr = ConvertToSql(context, e.Expression, false);
1239 return Convert(context, new SqlQuery.Predicate.IsNull(expr, true));
1240 }
1241
1242 break;
1243 }
1244
1245 case ExpressionType.TypeIs:
1246 {
1247 var e = (TypeBinaryExpression)expression;
1248 var ctx = GetContext(context, e.Expression);
1249
1250 if (ctx != null && ctx.IsExpression(e.Expression, 0, RequestFor.Table).Result)
1251 return MakeIsPredicate(ctx, e);
1252
1253 break;
1254 }
1255 }
1256
1257 var ex = ConvertToSql(context, expression, false);
1258
1259 if (SqlExpression.NeedsEqual(ex))
1260 return Convert(context, new SqlQuery.Predicate.ExprExpr(ex, SqlQuery.Predicate.Operator.Equal, new SqlValue(true)));
1261
1262 return Convert(context, new SqlQuery.Predicate.Expr(ex));
1263 }
1264
1265 #region ConvertCompare
1266
1267 ISqlPredicate ConvertCompare(IBuildContext context, ExpressionType nodeType, Expression left, Expression right)
1268 {
1269 if (left.NodeType == ExpressionType.Convert && left.Type == typeof(int) && right.NodeType == ExpressionType.Constant)
1270 {
1271 var conv = (UnaryExpression)left;
1272
1273 if (conv.Operand.Type == typeof(char))
1274 {
1275 left = conv.Operand;
1276 right = Expression.Constant(ConvertTo<char>.From(((ConstantExpression)right).Value));
1277 }
1278 }
1279
1280 if (right.NodeType == ExpressionType.Convert && right.Type == typeof(int) && left.NodeType == ExpressionType.Constant)
1281 {
1282 var conv = (UnaryExpression)right;
1283
1284 if (conv.Operand.Type == typeof(char))
1285 {
1286 right = conv.Operand;
1287 left = Expression.Constant(ConvertTo<char>.From(((ConstantExpression)left).Value));
1288 }
1289 }
1290
1291 #region special case for char?
1292
1293 // if (left.NodeType == ExpressionType.Convert && left.Type == typeof(int?) && right.NodeType == ExpressionType.Convert)
1294 // {
1295 // var convLeft = left as UnaryExpression;
1296 // var convRight = right as UnaryExpression;
1297 //
1298 // if (convLeft != null && convRight != null && convLeft.Operand.Type == typeof(char?))
1299 // {
1300 // left = convLeft.Operand;
1301 // right = Expression.Constant(ConvertTo<char?>.From(((ConstantExpression)convRight.Operand).Value));
1302 // }
1303 // }
1304
1305 #endregion
1306
1307 switch (nodeType)
1308 {
1309 case ExpressionType.Equal :
1310 case ExpressionType.NotEqual :
1311
1312 var p = ConvertObjectComparison(nodeType, context, left, context, right);
1313 if (p != null)
1314 return p;
1315
1316 p = ConvertObjectNullComparison(context, left, right, nodeType == ExpressionType.Equal);
1317 if (p != null)
1318 return p;
1319
1320 p = ConvertObjectNullComparison(context, right, left, nodeType == ExpressionType.Equal);
1321 if (p != null)
1322 return p;
1323
1324 if (left.NodeType == ExpressionType.New || right.NodeType == ExpressionType.New)
1325 {
1326 p = ConvertNewObjectComparison(context, nodeType, left, right);
1327 if (p != null)
1328 return p;
1329 }
1330
1331 break;
1332 }
1333
1334 SqlQuery.Predicate.Operator op;
1335
1336 switch (nodeType)
1337 {
1338 case ExpressionType.Equal : op = SqlQuery.Predicate.Operator.Equal; break;
1339 case ExpressionType.NotEqual : op = SqlQuery.Predicate.Operator.NotEqual; break;
1340 case ExpressionType.GreaterThan : op = SqlQuery.Predicate.Operator.Greater; break;
1341 case ExpressionType.GreaterThanOrEqual: op = SqlQuery.Predicate.Operator.GreaterOrEqual; break;
1342 case ExpressionType.LessThan : op = SqlQuery.Predicate.Operator.Less; break;
1343 case ExpressionType.LessThanOrEqual : op = SqlQuery.Predicate.Operator.LessOrEqual; break;
1344 default: throw new InvalidOperationException();
1345 }
1346
1347 if (left.NodeType == ExpressionType.Convert || right.NodeType == ExpressionType.Convert
1348 || left.NodeType == ExpressionType.MemberAccess || right.NodeType == ExpressionType.MemberAccess)
1349 {
1350 var p = ConvertEnumConversion(context, left, op, right);
1351 if (p != null)
1352 return p;
1353 }
1354
1355 var l = ConvertToSql(context, left, false);
1356 var r = ConvertToSql(context, right, true);
1357
1358 switch (nodeType)
1359 {
1360 case ExpressionType.Equal :
1361 case ExpressionType.NotEqual:
1362
1363 if (!context.SqlQuery.IsParameterDependent && (l is SqlParameter || r is SqlParameter) && l.CanBeNull() && r.CanBeNull())
1364 context.SqlQuery.IsParameterDependent = true;
1365
1366 // | (SqlQuery(Select([]) as q), SqlValue(null))
1367 // | (SqlValue(null), SqlQuery(Select([]) as q)) =>
1368
1369 SqlQuery q =
1370 l.ElementType == QueryElementType.SqlQuery &&
1371 r.ElementType == QueryElementType.SqlValue &&
1372 ((SqlValue)r).Value == null &&
1373 ((SqlQuery)l).Select.Columns.Count == 0 ?
1374 (SqlQuery)l :
1375 r.ElementType == QueryElementType.SqlQuery &&
1376 l.ElementType == QueryElementType.SqlValue &&
1377 ((SqlValue)l).Value == null &&
1378 ((SqlQuery)r).Select.Columns.Count == 0 ?
1379 (SqlQuery)r :
1380 null;
1381
1382 if (q != null)
1383 {
1384 q.Select.Columns.Add(new SqlQuery.Column(q, new SqlValue(1)));
1385 }
1386
1387 break;
1388 }
1389
1390 if (l is SqlQuery.SearchCondition)
1391 l = Convert(context, new SqlFunction(typeof(bool), "CASE", l, new SqlValue(true), new SqlValue(false)));
1392
1393 if (r is SqlQuery.SearchCondition)
1394 r = Convert(context, new SqlFunction(typeof(bool), "CASE", r, new SqlValue(true), new SqlValue(false)));
1395
1396 return Convert(context, new SqlQuery.Predicate.ExprExpr(l, op, r));
1397 }
1398
1399 #endregion
1400
1401 #region ConvertEnumConversion
1402
1403 ISqlPredicate ConvertEnumConversion(IBuildContext context, Expression left, SqlQuery.Predicate.Operator op, Expression right)
1404 {
1405 Expression value;
1406 Expression operand;
1407
1408 if (left is MemberExpression)
1409 {
1410 operand = left;
1411 value = right;
1412 }
1413 else if (left.NodeType == ExpressionType.Convert && ((UnaryExpression)left).Operand is MemberExpression)
1414 {
1415 operand = ((UnaryExpression)left).Operand;
1416 value = right;
1417 }
1418 else if (right is MemberExpression)
1419 {
1420 operand = right;
1421 value = left;
1422 }
1423 else if (right.NodeType == ExpressionType.Convert && ((UnaryExpression)right).Operand is MemberExpression)
1424 {
1425 operand = ((UnaryExpression)right).Operand;
1426 value = left;
1427 }
1428 else if (left.NodeType == ExpressionType.Convert)
1429 {
1430 operand = ((UnaryExpression)left).Operand;
1431 value = right;
1432 }
1433 else
1434 {
1435 operand = ((UnaryExpression)right).Operand;
1436 value = left;
1437 }
1438
1439 var type = operand.Type;
1440
1441 if (!TypeHelper.IsEnumOrNullableEnum(type))
1442 return null;
1443
1444 var dic = new Dictionary<object,object>();
1445
1446 var nullValue = MappingSchema.GetNullValue(type);
1447
1448 if (nullValue != null)
1449 dic.Add(nullValue, null);
1450
1451 var mapValues = MappingSchema.GetMapValues(type);
1452
1453 if (mapValues != null)
1454 foreach (var mv in mapValues)
1455 if (!dic.ContainsKey(mv.OrigValue))
1456 dic.Add(mv.OrigValue, mv.MapValues[0]);
1457
1458 switch (value.NodeType)
1459 {
1460 case ExpressionType.Constant:
1461 {
1462 var name = Enum.GetName(type, ((ConstantExpression)value).Value);
1463
1464 // ReSharper disable ConditionIsAlwaysTrueOrFalse
1465 // ReSharper disable HeuristicUnreachableCode
1466 if (name == null)
1467 return null;
1468 // ReSharper restore HeuristicUnreachableCode
1469 // ReSharper restore ConditionIsAlwaysTrueOrFalse
1470
1471 var origValue = Enum.Parse(type, name, false);
1472 var mapValue = origValue;
1473
1474 if (!(operand is MemberExpression))
1475 {
1476 if (!dic.TryGetValue(origValue, out mapValue))
1477 return null;
1478 }
1479
1480 ISqlExpression l, r;
1481
1482 SqlValue sqlValue;
1483
1484 if (left.NodeType == ExpressionType.Convert)
1485 {
1486 l = ConvertToSql(context, operand, false);
1487 r = sqlValue = new SqlValue(mapValue);
1488 }
1489 else
1490 {
1491 r = ConvertToSql(context, operand, false);
1492 l = sqlValue = new SqlValue(mapValue);
1493 }
1494
1495 if (operand is MemberExpression)
1496 {
1497 var me = (MemberExpression)operand;
1498 var memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name];
1499 sqlValue.SetEnumConverter(memberAccessor, MappingSchema);
1500 }
1501
1502
1503 return Convert(context, new SqlQuery.Predicate.ExprExpr(l, op, r));
1504 }
1505
1506 case ExpressionType.Convert:
1507 {
1508 value = ((UnaryExpression)value).Operand;
1509
1510 var l = ConvertToSql(context, operand, false, false);
1511 var r = ConvertToSql(context, value, false, false);
1512
1513 MemberAccessor memberAccessor = null;
1514
1515 if (operand is MemberExpression)
1516 {
1517 // is it even possible that operand is not MemberExpression?
1518 // if no, then we can remove this two last uses of SetEnumConverter(type, map)
1519 // and other depending code
1520 // At least currently there is no test coverage for this method and I didn't
1521 // manage to create such test
1522 var me = (MemberExpression)operand;
1523 memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name];
1524 }
1525
1526 if (l is SqlValueBase)
1527 {
1528 if (memberAccessor != null)
1529 {
1530 ((SqlValueBase)l).SetEnumConverter(memberAccessor, MappingSchema);
1531 }
1532 else
1533 {
1534 ((SqlValueBase)l).SetEnumConverter(type, MappingSchema);
1535 }
1536 }
1537
1538 if (r is SqlValueBase)
1539 {
1540 if (memberAccessor != null)
1541 {
1542 ((SqlValueBase)r).SetEnumConverter(memberAccessor, MappingSchema);
1543 }
1544 else
1545 {
1546 ((SqlValueBase)r).SetEnumConverter(type, MappingSchema);
1547 }
1548 }
1549
1550 return Convert(context, new SqlQuery.Predicate.ExprExpr(l, op, r));
1551 }
1552 }
1553
1554 return null;
1555 }
1556
1557 #endregion
1558
1559 #region ConvertObjectNullComparison
1560
1561 ISqlPredicate ConvertObjectNullComparison(IBuildContext context, Expression left, Expression right, bool isEqual)
1562 {
1563 if (right.NodeType == ExpressionType.Constant && ((ConstantExpression)right).Value == null)
1564 {
1565 if (left.NodeType == ExpressionType.MemberAccess || left.NodeType == ExpressionType.Parameter)
1566 {
1567 var ctx = GetContext(context, left);
1568
1569 if (ctx != null && ctx.IsExpression(left, 0, RequestFor.Object).Result ||
1570 left.NodeType == ExpressionType.Parameter && ctx.IsExpression(left, 0, RequestFor.Field).Result)
1571 {
1572 return new SqlQuery.Predicate.Expr(new SqlValue(!isEqual));
1573 }
1574 }
1575 }
1576
1577 return null;
1578 }
1579
1580 #endregion
1581
1582 #region ConvertObjectComparison
1583
1584 public ISqlPredicate ConvertObjectComparison(
1585 ExpressionType nodeType,
1586 IBuildContext leftContext,
1587 Expression left,
1588 IBuildContext rightContext,
1589 Expression right)
1590 {
1591 var qsl = GetContext(leftContext, left);
1592 var qsr = GetContext(rightContext, right);
1593
1594 var sl = qsl != null && qsl.IsExpression(left, 0, RequestFor.Object).Result;
1595 var sr = qsr != null && qsr.IsExpression(right, 0, RequestFor.Object).Result;
1596
1597 bool isNull;
1598 SqlInfo[] lcols;
1599
1600 var rmembers = new Dictionary<MemberInfo,Expression>(new MemberInfoComparer());
1601
1602 if (sl == false && sr == false)
1603 {
1604 var lmembers = new Dictionary<MemberInfo,Expression>(new MemberInfoComparer());
1605
1606 if (!ProcessProjection(lmembers, left) && !ProcessProjection(rmembers, right))
1607 return null;
1608
1609 if (lmembers.Count == 0)
1610 {
1611 var r = right;
1612 right = left;
1613 left = r;
1614
1615 var c = rightContext;
1616 rightContext = leftContext;
1617 leftContext = c;
1618
1619 var q = qsr;
1620 qsl = q;
1621
1622 sr = false;
1623
1624 var lm = lmembers;
1625 lmembers = rmembers;
1626 rmembers = lm;
1627 }
1628
1629 isNull = right is ConstantExpression && ((ConstantExpression)right).Value == null;
1630 lcols = lmembers.Select(m => new SqlInfo(m.Key) { Sql = ConvertToSql(leftContext, m.Value, false) }).ToArray();
1631 }
1632 else
1633 {
1634 if (sl == false)
1635 {
1636 var r = right;
1637 right = left;
1638 left = r;
1639
1640 var c = rightContext;
1641 rightContext = leftContext;
1642 leftContext = c;
1643
1644 var q = qsr;
1645 qsl = q;
1646
1647 sr = false;
1648 }
1649
1650 isNull = right is ConstantExpression && ((ConstantExpression)right).Value == null;
1651 lcols = qsl.ConvertToSql(left, 0, ConvertFlags.Key);
1652
1653 if (!sr)
1654 ProcessProjection(rmembers, right);
1655 }
1656
1657 if (lcols.Length == 0)
1658 return null;
1659
1660 var condition = new SqlQuery.SearchCondition();
1661
1662 foreach (var lcol in lcols)
1663 {
1664 if (lcol.Members.Count == 0)
1665 throw new InvalidOperationException();
1666
1667 ISqlExpression rcol = null;
1668
1669 var lmember = lcol.Members[lcol.Members.Count - 1];
1670
1671 if (sr)
1672 {
1673 var info = rightContext.ConvertToSql(Expression.MakeMemberAccess(right, lmember), 0, ConvertFlags.Field).Single();
1674 rcol = info.Sql;
1675 }
1676 else
1677 {
1678 if (rmembers.Count != 0)
1679 {
1680 var info = rightContext.ConvertToSql(rmembers[lmember], 0, ConvertFlags.Field)[0];
1681 rcol = info.Sql;
1682 }
1683 }
1684
1685 var rex =
1686 isNull ?
1687 new SqlValue(right.Type, null) :
1688 rcol ?? GetParameter(right, lmember);
1689
1690 var predicate = Convert(leftContext, new SqlQuery.Predicate.ExprExpr(
1691 lcol.Sql,
1692 nodeType == ExpressionType.Equal ? SqlQuery.Predicate.Operator.Equal : SqlQuery.Predicate.Operator.NotEqual,
1693 rex));
1694
1695 condition.Conditions.Add(new SqlQuery.Condition(false, predicate));
1696 }
1697
1698 if (nodeType == ExpressionType.NotEqual)
1699 foreach (var c in condition.Conditions)
1700 c.IsOr = true;
1701
1702 return condition;
1703 }
1704
1705 ISqlPredicate ConvertNewObjectComparison(IBuildContext context, ExpressionType nodeType, Expression left, Expression right)
1706 {
1707 left = FindExpression(left);
1708 right = FindExpression(right);
1709
1710 var condition = new SqlQuery.SearchCondition();
1711
1712 if (left.NodeType != ExpressionType.New)
1713 {
1714 var temp = left;
1715 left = right;
1716 right = temp;
1717 }
1718
1719 var newRight = right as NewExpression;
1720 var newExpr = (NewExpression)left;
1721
1722 // ReSharper disable ConditionIsAlwaysTrueOrFalse
1723 // ReSharper disable HeuristicUnreachableCode
1724 if (newExpr.Members == null)
1725 return null;
1726 // ReSharper restore HeuristicUnreachableCode
1727 // ReSharper restore ConditionIsAlwaysTrueOrFalse
1728
1729 for (var i = 0; i < newExpr.Arguments.Count; i++)
1730 {
1731 var lex = ConvertToSql(context, newExpr.Arguments[i], false);
1732 var rex =
1733 newRight != null ?
1734 ConvertToSql(context, newRight.Arguments[i], false) :
1735 GetParameter(right, newExpr.Members[i]);
1736
1737 var predicate = Convert(context,
1738 new SqlQuery.Predicate.ExprExpr(
1739 lex,
1740 nodeType == ExpressionType.Equal ? SqlQuery.Predicate.Operator.Equal : SqlQuery.Predicate.Operator.NotEqual,
1741 rex));
1742
1743 condition.Conditions.Add(new SqlQuery.Condition(false, predicate));
1744 }
1745
1746 if (nodeType == ExpressionType.NotEqual)
1747 foreach (var c in condition.Conditions)
1748 c.IsOr = true;
1749
1750 return condition;
1751 }
1752
1753 ISqlExpression GetParameter(Expression ex, MemberInfo member)
1754 {
1755 if (member is MethodInfo)
1756 member = TypeHelper.GetPropertyByMethod((MethodInfo)member);
1757
1758 var par = ReplaceParameter(_expressionAccessors, ex, _ => {});
1759 var expr = Expression.MakeMemberAccess(par.Type == typeof(object) ? Expression.Convert(par, member.DeclaringType) : par, member);
1760 var mapper = Expression.Lambda<Func<Expression,object[],object>>(
1761 Expression.Convert(expr, typeof(object)),
1762 new [] { ExpressionParam, ParametersParam });
1763
1764 var p = new ParameterAccessor
1765 {
1766 Expression = expr,
1767 Accessor = mapper.Compile(),
1768 SqlParameter = new SqlParameter(expr.Type, member.Name, null, MappingSchema)
1769 };
1770
1771 _parameters.Add(expr, p);
1772 CurrentSqlParameters.Add(p);
1773
1774 return p.SqlParameter;
1775 }
1776
1777 static Expression FindExpression(Expression expr)
1778 {
1779 var ret = expr.Find(pi =>
1780 {
1781 switch (pi.NodeType)
1782 {
1783 case ExpressionType.Convert :
1784 {
1785 var e = (UnaryExpression)expr;
1786
1787 return
1788 e.Operand.NodeType == ExpressionType.ArrayIndex &&
1789 ((BinaryExpression)e.Operand).Left == ParametersParam;
1790 }
1791
1792 case ExpressionType.MemberAccess :
1793 case ExpressionType.New :
1794 return true;
1795 }
1796
1797 return false;
1798 });
1799
1800 if (ret == null)
1801 throw new InvalidOperationException();
1802
1803 return ret;
1804 }
1805
1806 #endregion
1807
1808 #region ConvertInPredicate
1809
1810 private ISqlPredicate ConvertInPredicate(IBuildContext context, MethodCallExpression expression)
1811 {
1812 var e = expression;
1813 var argIndex = e.Object != null ? 0 : 1;
1814 var arr = e.Object ?? e.Arguments[0];
1815 var arg = e.Arguments[argIndex];
1816
1817 ISqlExpression expr = null;
1818
1819 var ctx = GetContext(context, arg);
1820
1821 if (ctx is TableBuilder.TableContext &&
1822 ctx.SqlQuery != context.SqlQuery &&
1823 ctx.IsExpression(arg, 0, RequestFor.Object).Result)
1824 {
1825 expr = ctx.SqlQuery;
1826 }
1827
1828 if (expr == null)
1829 {
1830 var sql = ConvertExpressions(context, arg, ConvertFlags.Key);
1831
1832 if (sql.Length == 1 && sql[0].Members.Count == 0)
1833 expr = sql[0].Sql;
1834 else
1835 expr = new SqlExpression(
1836 '\x1' + string.Join(",", sql.Select(s => s.Members[s.Members.Count - 1].Name).ToArray()),
1837 sql.Select(s => s.Sql).ToArray());
1838 }
1839
1840 MemberAccessor memberAccessor = null;
1841
1842 if (arg is MemberExpression)
1843 {
1844 var me = (MemberExpression)arg;
1845 if (TypeHelper.IsEnumOrNullableEnum(me.Type))
1846 {
1847 memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name];
1848 }
1849 }
1850
1851 switch (arr.NodeType)
1852 {
1853 case ExpressionType.NewArrayInit :
1854 {
1855 var newArr = (NewArrayExpression)arr;
1856
1857 if (newArr.Expressions.Count == 0)
1858 return new SqlQuery.Predicate.Expr(new SqlValue(false));
1859
1860 var exprs = new ISqlExpression[newArr.Expressions.Count];
1861
1862 for (var i = 0; i < newArr.Expressions.Count; i++)
1863 {
1864 exprs[i] = ConvertToSql(context, newArr.Expressions[i], false, false);
1865
1866 if (memberAccessor != null && exprs[i] is SqlValue)
1867 {
1868 ((SqlValue)exprs[i]).SetEnumConverter(memberAccessor, MappingSchema);
1869 }
1870 }
1871
1872 return new SqlQuery.Predicate.InList(expr, false, exprs);
1873 }
1874
1875 default :
1876
1877 if (CanBeCompiled(arr))
1878 {
1879 var p = BuildParameter(arr).SqlParameter;
1880 p.IsQueryParameter = false;
1881 if (memberAccessor != null)
1882 {
1883 p.SetEnumConverter(memberAccessor, MappingSchema);
1884 }
1885 return new SqlQuery.Predicate.InList(expr, false, p);
1886 }
1887
1888 break;
1889 }
1890
1891 throw new LinqException("'{0}' cannot be converted to SQL.", expression);
1892 }
1893
1894 #endregion
1895
1896 #region LIKE predicate
1897
1898 ISqlPredicate ConvertLikePredicate(IBuildContext context, MethodCallExpression expression, string start, string end)
1899 {
1900 var e = expression;
1901 var o = ConvertToSql(context, e.Object, false);
1902 var a = ConvertToSql(context, e.Arguments[0], false);
1903
1904 if (a is SqlValue)
1905 {
1906 var value = ((SqlValue)a).Value;
1907
1908 if (value == null)
1909 throw new LinqException("NULL cannot be used as a LIKE predicate parameter.");
1910
1911 return value.ToString().IndexOfAny(new[] { '%', '_' }) < 0?
1912 new SqlQuery.Predicate.Like(o, false, new SqlValue(start + value + end), null):
1913 new SqlQuery.Predicate.Like(o, false, new SqlValue(start + EscapeLikeText(value.ToString()) + end), new SqlValue('~'));
1914 }
1915
1916 if (a is SqlParameter)
1917 {
1918 var p = (SqlParameter)a;
1919 var ep = (from pm in CurrentSqlParameters where pm.SqlParameter == p select pm).First();
1920
1921 ep = new ParameterAccessor
1922 {
1923 Expression = ep.Expression,
1924 Accessor = ep.Accessor,
1925 SqlParameter = new SqlParameter(ep.Expression.Type, p.Name, p.Value, GetLikeEscaper(start, end))
1926 };
1927
1928 CurrentSqlParameters.Add(ep);
1929
1930 return new SqlQuery.Predicate.Like(o, false, ep.SqlParameter, new SqlValue('~'));
1931 }
1932
1933 var mi = ReflectionHelper.Expressor<string>.MethodExpressor(_ => _.Replace("", ""));
1934 var ex =
1935 Expression.Call(
1936 Expression.Call(
1937 Expression.Call(
1938 e.Arguments[0],
1939 mi, Expression.Constant("~"), Expression.Constant("~~")),
1940 mi, Expression.Constant("%"), Expression.Constant("~%")),
1941 mi, Expression.Constant("_"), Expression.Constant("~_"));
1942
1943 var expr = ConvertToSql(context, ConvertExpression(ex), false);
1944
1945 if (!string.IsNullOrEmpty(start))
1946 expr = new SqlBinaryExpression(typeof(string), new SqlValue("%"), "+", expr);
1947
1948 if (!string.IsNullOrEmpty(end))
1949 expr = new SqlBinaryExpression(typeof(string), expr, "+", new SqlValue("%"));
1950
1951 return new SqlQuery.Predicate.Like(o, false, expr, new SqlValue('~'));
1952 }
1953
1954 ISqlPredicate ConvertLikePredicate(IBuildContext context, MethodCallExpression expression)
1955 {
1956 var e = expression;
1957 var a1 = ConvertToSql(context, e.Arguments[0], false);
1958 var a2 = ConvertToSql(context, e.Arguments[1], false);
1959
1960 ISqlExpression a3 = null;
1961
1962 if (e.Arguments.Count == 3)
1963 a3 = ConvertToSql(context, e.Arguments[2], false);
1964
1965 return new SqlQuery.Predicate.Like(a1, false, a2, a3);
1966 }
1967
1968 static string EscapeLikeText(string text)
1969 {
1970 if (text.IndexOfAny(new[] { '%', '_' }) < 0)
1971 return text;
1972
1973 var builder = new StringBuilder(text.Length);
1974
1975 foreach (var ch in text)
1976 {
1977 switch (ch)
1978 {
1979 case '%':
1980 case '_':
1981 case '~':
1982 builder.Append('~');
1983 break;
1984 }
1985
1986 builder.Append(ch);
1987 }
1988
1989 return builder.ToString();
1990 }
1991
1992 static Converter<object,object> GetLikeEscaper(string start, string end)
1993 {
1994 return value => value == null? null: start + EscapeLikeText(value.ToString()) + end;
1995 }
1996
1997 #endregion
1998
1999 #region MakeIsPredicate
2000
2001 internal ISqlPredicate MakeIsPredicate(TableBuilder.TableContext table, Type typeOperand)
2002 {
2003 if (typeOperand == table.ObjectType && !table.InheritanceMapping.Any(m => m.Type == typeOperand))
2004 return Convert(table, new SqlQuery.Predicate.Expr(new SqlValue(true)));
2005
2006 return MakeIsPredicate(
2007 table, table.InheritanceMapping, table.InheritanceDiscriminators, typeOperand,
2008 name => table.SqlTable.Fields.Values.First(f => f.Name == name));
2009 }
2010
2011 internal ISqlPredicate MakeIsPredicate(
2012 IBuildContext context,
2013 List<InheritanceMappingAttribute> inheritanceMapping,
2014 List<string> inheritanceDiscriminators,
2015 Type toType,
2016 Func<string,ISqlExpression> getSql)
2017 {
2018 var mapping = inheritanceMapping
2019 .Select((m,i) => new { m, i })
2020 .Where ( m => m.m.Type == toType && !m.m.IsDefault)
2021 .ToList();
2022
2023 switch (mapping.Count)
2024 {
2025 case 0 :
2026 {
2027 var cond = new SqlQuery.SearchCondition();
2028
2029 foreach (var m in inheritanceMapping.Select((m,i) => new { m, i }).Where(m => !m.m.IsDefault))
2030 {
2031 cond.Conditions.Add(
2032 new SqlQuery.Condition(
2033 false,
2034 Convert(context,
2035 new SqlQuery.Predicate.ExprExpr(
2036 getSql(inheritanceDiscriminators[m.i]),
2037 SqlQuery.Predicate.Operator.NotEqual,
2038 new SqlValue(m.m.Code)))));
2039 }
2040
2041 return cond;
2042 }
2043
2044 case 1 :
2045 return Convert(context,
2046 new SqlQuery.Predicate.ExprExpr(
2047 getSql(inheritanceDiscriminators[mapping[0].i]),
2048 SqlQuery.Predicate.Operator.Equal,
2049 new SqlValue(mapping[0].m.Code)));
2050
2051 default:
2052 {
2053 var cond = new SqlQuery.SearchCondition();
2054
2055 foreach (var m in mapping)
2056 {
2057 cond.Conditions.Add(
2058 new SqlQuery.Condition(
2059 false,
2060 Convert(context,
2061 new SqlQuery.Predicate.ExprExpr(
2062 getSql(inheritanceDiscriminators[m.i]),
2063 SqlQuery.Predicate.Operator.Equal,
2064 new SqlValue(m.m.Code))),
2065 true));
2066 }
2067
2068 return cond;
2069 }
2070 }
2071 }
2072
2073 ISqlPredicate MakeIsPredicate(IBuildContext context, TypeBinaryExpression expression)
2074 {
2075 var typeOperand = expression.TypeOperand;
2076 var table = new TableBuilder.TableContext(this, new BuildInfo((IBuildContext)null, Expression.Constant(null), new SqlQuery()), typeOperand);
2077
2078 if (typeOperand == table.ObjectType && !table.InheritanceMapping.Any(m => m.Type == typeOperand))
2079 return Convert(table, new SqlQuery.Predicate.Expr(new SqlValue(true)));
2080
2081 var mapping = table.InheritanceMapping.Select((m,i) => new { m, i }).Where(m => m.m.Type == typeOperand && !m.m.IsDefault).ToList();
2082 var isEqual = true;
2083
2084 if (mapping.Count == 0)
2085 {
2086 mapping = table.InheritanceMapping.Select((m,i) => new { m, i }).Where(m => !m.m.IsDefault).ToList();
2087 isEqual = false;
2088 }
2089
2090 Expression expr = null;
2091
2092 foreach (var m in mapping)
2093 {
2094 var field = table.SqlTable.Fields[table.InheritanceDiscriminators[m.i]];
2095 var ttype = field.MemberMapper.MemberAccessor.TypeAccessor.OriginalType;
2096 var obj = expression.Expression;
2097
2098 if (obj.Type != ttype)
2099 obj = Expression.Convert(expression.Expression, ttype);
2100
2101 var left = Expression.PropertyOrField(obj, field.Name);
2102 var code = m.m.Code;
2103
2104 if (code == null)
2105 code = TypeHelper.GetDefaultValue(left.Type);
2106 else if (left.Type != code.GetType())
2107 code = MappingSchema.ConvertChangeType(code, left.Type);
2108
2109 Expression right = Expression.Constant(code, left.Type);
2110
2111 var e = isEqual ? Expression.Equal(left, right) : Expression.NotEqual(left, right);
2112
2113 expr = expr != null ? Expression.AndAlso(expr, e) : e;
2114 }
2115
2116 return ConvertPredicate(context, expr);
2117 }
2118
2119 #endregion
2120
2121 #endregion
2122
2123 #region Search Condition Builder
2124
2125 void BuildSearchCondition(IBuildContext context, Expression expression, List<SqlQuery.Condition> conditions)
2126 {
2127 switch (expression.NodeType)
2128 {
2129 case ExpressionType.And :
2130 case ExpressionType.AndAlso :
2131 {
2132 var e = (BinaryExpression)expression;
2133
2134 BuildSearchCondition(context, e.Left, conditions);
2135 BuildSearchCondition(context, e.Right, conditions);
2136
2137 break;
2138 }
2139
2140 case ExpressionType.Or :
2141 case ExpressionType.OrElse :
2142 {
2143 var e = (BinaryExpression)expression;
2144 var orCondition = new SqlQuery.SearchCondition();
2145
2146 BuildSearchCondition(context, e.Left, orCondition.Conditions);
2147 orCondition.Conditions[orCondition.Conditions.Count - 1].IsOr = true;
2148 BuildSearchCondition(context, e.Right, orCondition.Conditions);
2149
2150 conditions.Add(new SqlQuery.Condition(false, orCondition));
2151
2152 break;
2153 }
2154
2155 case ExpressionType.Not :
2156 {
2157 var e = expression as UnaryExpression;
2158 var notCondition = new SqlQuery.SearchCondition();
2159
2160 BuildSearchCondition(context, e.Operand, notCondition.Conditions);
2161
2162 if (notCondition.Conditions.Count == 1 && notCondition.Conditions[0].Predicate is SqlQuery.Predicate.NotExpr)
2163 {
2164 var p = notCondition.Conditions[0].Predicate as SqlQuery.Predicate.NotExpr;
2165 p.IsNot = !p.IsNot;
2166 conditions.Add(notCondition.Conditions[0]);
2167 }
2168 else
2169 conditions.Add(new SqlQuery.Condition(true, notCondition));
2170
2171 break;
2172 }
2173
2174 default :
2175 var predicate = ConvertPredicate(context, expression);
2176
2177 if (predicate is SqlQuery.Predicate.Expr)
2178 {
2179 var expr = ((SqlQuery.Predicate.Expr)predicate).Expr1;
2180
2181 if (expr.ElementType == QueryElementType.SearchCondition)
2182 {
2183 var sc = (SqlQuery.SearchCondition)expr;
2184
2185 if (sc.Conditions.Count == 1)
2186 {
2187 conditions.Add(sc.Conditions[0]);
2188 break;
2189 }
2190 }
2191 }
2192
2193 conditions.Add(new SqlQuery.Condition(false, predicate));
2194
2195 break;
2196 }
2197 }
2198
2199 #endregion
2200
2201 #region CanBeTranslatedToSql
2202
2203 bool CanBeTranslatedToSql(IBuildContext context, Expression expr, bool canBeCompiled)
2204 {
2205 List<Expression> ignoredMembers = null;
2206
2207 return null == expr.Find(pi =>
2208 {
2209 if (ignoredMembers != null)
2210 {
2211 if (pi != ignoredMembers[ignoredMembers.Count - 1])
2212 throw new InvalidOperationException();
2213
2214 if (ignoredMembers.Count == 1)
2215 ignoredMembers = null;
2216 else
2217 ignoredMembers.RemoveAt(ignoredMembers.Count - 1);
2218
2219 return false;
2220 }
2221
2222 switch (pi.NodeType)
2223 {
2224 case ExpressionType.MemberAccess :
2225 {
2226 var ma = (MemberExpression)pi;
2227 var attr = GetFunctionAttribute(ma.Member);
2228
2229 if (attr == null && !TypeHelper.IsNullableValueMember(ma.Member))
2230 {
2231 if (canBeCompiled)
2232 {
2233 var ctx = GetContext(context, pi);
2234
2235 if (ctx == null)
2236 return !CanBeCompiled(pi);
2237
2238 if (ctx.IsExpression(pi, 0, RequestFor.Object).Result)
2239 return !CanBeCompiled(pi);
2240
2241 ignoredMembers = ma.Expression.GetMembers();
2242 }
2243 }
2244
2245 break;
2246 }
2247
2248 case ExpressionType.Parameter :
2249 {
2250 var ctx = GetContext(context, pi);
2251
2252 if (ctx == null)
2253 if (canBeCompiled)
2254 return !CanBeCompiled(pi);
2255
2256 break;
2257 }
2258
2259 case ExpressionType.Call :
2260 {
2261 var e = (MethodCallExpression)pi;
2262
2263 if (e.Method.DeclaringType != typeof(Enumerable))
2264 {
2265 var attr = GetFunctionAttribute(e.Method);
2266
2267 if (attr == null && canBeCompiled)
2268 return !CanBeCompiled(pi);
2269 }
2270
2271 break;
2272 }
2273
2274 case ExpressionType.TypeIs : return canBeCompiled;
2275 case ExpressionType.TypeAs :
2276 case ExpressionType.New : return true;
2277
2278 case ExpressionType.NotEqual :
2279 case ExpressionType.Equal :
2280 {
2281 var e = (BinaryExpression)pi;
2282
2283 Expression obj = null;
2284
2285 if (e.Left.NodeType == ExpressionType.Constant && ((ConstantExpression)e.Left).Value == null)
2286 obj = e.Right;
2287 else if (e.Right.NodeType == ExpressionType.Constant && ((ConstantExpression)e.Right).Value == null)
2288 obj = e.Left;
2289
2290 if (obj != null)
2291 {
2292 var ctx = GetContext(context, obj);
2293
2294 if (ctx != null)
2295 {
2296 if (ctx.IsExpression(obj, 0, RequestFor.Table). Result ||
2297 ctx.IsExpression(obj, 0, RequestFor.Association).Result)
2298 {
2299 ignoredMembers = obj.GetMembers();
2300 }
2301 }
2302 }
2303
2304 break;
2305 }
2306 }
2307
2308 return false;
2309 });
2310 }
2311
2312 #endregion
2313
2314 #region Helpers
2315
2316 public IBuildContext GetContext([JetBrains.Annotations.NotNull] IBuildContext current, Expression expression)
2317 {
2318 var root = expression.GetRootObject();
2319
2320 for (; current != null; current = current.Parent)
2321 if (current.IsExpression(root, 0, RequestFor.Root).Result)
2322 return current;
2323
2324 return null;
2325 }
2326
2327 SqlFunctionAttribute GetFunctionAttribute(ICustomAttributeProvider member)
2328 {
2329 var attrs = member.GetCustomAttributes(typeof(SqlFunctionAttribute), true);
2330
2331 if (attrs.Length == 0)
2332 return null;
2333
2334 SqlFunctionAttribute attr = null;
2335
2336 foreach (SqlFunctionAttribute a in attrs)
2337 {
2338 if (a.SqlProvider == SqlProvider.Name)
2339 {
2340 attr = a;
2341 break;
2342 }
2343
2344 if (a.SqlProvider == null)
2345 attr = a;
2346 }
2347
2348 return attr;
2349 }
2350
2351 internal TableFunctionAttribute GetTableFunctionAttribute(ICustomAttributeProvider member)
2352 {
2353 var attrs = member.GetCustomAttributes(typeof(TableFunctionAttribute), true);
2354
2355 if (attrs.Length == 0)
2356 return null;
2357
2358 TableFunctionAttribute attr = null;
2359
2360 foreach (TableFunctionAttribute a in attrs)
2361 {
2362 if (a.SqlProvider == SqlProvider.Name)
2363 {
2364 attr = a;
2365 break;
2366 }
2367
2368 if (a.SqlProvider == null)
2369 attr = a;
2370 }
2371
2372 return attr;
2373 }
2374
2375 public ISqlExpression Convert(IBuildContext context, ISqlExpression expr)
2376 {
2377 SqlProvider.SqlQuery = context.SqlQuery;
2378 return SqlProvider.ConvertExpression(expr);
2379 }
2380
2381 public ISqlPredicate Convert(IBuildContext context, ISqlPredicate predicate)
2382 {
2383 SqlProvider.SqlQuery = context.SqlQuery;
2384 return SqlProvider.ConvertPredicate(predicate);
2385 }
2386
2387 public ISqlExpression ConvertTimeSpanMember(IBuildContext context, MemberExpression expression)
2388 {
2389 if (expression.Member.DeclaringType == typeof(TimeSpan))
2390 {
2391 switch (expression.Expression.NodeType)
2392 {
2393 case ExpressionType.Subtract :
2394 case ExpressionType.SubtractChecked:
2395
2396 Sql.DateParts datePart;
2397
2398 switch (expression.Member.Name)
2399 {
2400 case "TotalMilliseconds" : datePart = Sql.DateParts.Millisecond; break;
2401 case "TotalSeconds" : datePart = Sql.DateParts.Second; break;
2402 case "TotalMinutes" : datePart = Sql.DateParts.Minute; break;
2403 case "TotalHours" : datePart = Sql.DateParts.Hour; break;
2404 case "TotalDays" : datePart = Sql.DateParts.Day; break;
2405 default : return null;
2406 }
2407
2408 var e = (BinaryExpression)expression.Expression;
2409
2410 return new SqlFunction(
2411 typeof(int),
2412 "DateDiff",
2413 new SqlValue(datePart),
2414 ConvertToSql(context, e.Right, false),
2415 ConvertToSql(context, e.Left, false));
2416 }
2417 }
2418
2419 return null;
2420 }
2421
2422 internal ISqlExpression ConvertSearchCondition(IBuildContext context, ISqlExpression sqlExpression)
2423 {
2424 if (sqlExpression is SqlQuery.SearchCondition)
2425 {
2426 if (sqlExpression.CanBeNull())
2427 {
2428 var notExpr = new SqlQuery.SearchCondition
2429 {
2430 Conditions = { new SqlQuery.Condition(true, new SqlQuery.Predicate.Expr(sqlExpression, sqlExpression.Precedence)) }
2431 };
2432
2433 return Convert(context, new SqlFunction(sqlExpression.SystemType, "CASE", sqlExpression, new SqlValue(1), notExpr, new SqlValue(0), new SqlValue(null)));
2434 }
2435
2436 return Convert(context, new SqlFunction(sqlExpression.SystemType, "CASE", sqlExpression, new SqlValue(1), new SqlValue(0)));
2437 }
2438
2439 return sqlExpression;
2440 }
2441
2442 public bool ProcessProjection(Dictionary<MemberInfo,Expression> members, Expression expression)
2443 {
2444 switch (expression.NodeType)
2445 {
2446 // new { ... }
2447 //
2448 case ExpressionType.New :
2449 {
2450 var expr = (NewExpression)expression;
2451
2452 // ReSharper disable ConditionIsAlwaysTrueOrFalse
2453 // ReSharper disable HeuristicUnreachableCode
2454 if (expr.Members == null)
2455 return false;
2456 // ReSharper restore HeuristicUnreachableCode
2457 // ReSharper restore ConditionIsAlwaysTrueOrFalse
2458
2459 for (var i = 0; i < expr.Members.Count; i++)
2460 {
2461 var member = expr.Members[i];
2462
2463 members.Add(member, expr.Arguments[i]);
2464
2465 if (member is MethodInfo)
2466 members.Add(TypeHelper.GetPropertyByMethod((MethodInfo)member), expr.Arguments[i]);
2467 }
2468
2469 return true;
2470 }
2471
2472 // new MyObject { ... }
2473 //
2474 case ExpressionType.MemberInit :
2475 {
2476 var expr = (MemberInitExpression)expression;
2477 var dic = TypeAccessor.GetAccessor(expr.Type)
2478 .Select((m,i) => new { m, i })
2479 .ToDictionary(_ => _.m.MemberInfo.Name, _ => _.i);
2480
2481 foreach (var binding in expr.Bindings.Cast<MemberAssignment>().OrderBy(b => dic.ContainsKey(b.Member.Name) ? dic[b.Member.Name] : 1000000))
2482 {
2483 members.Add(binding.Member, binding.Expression);
2484
2485 if (binding.Member is MethodInfo)
2486 members.Add(TypeHelper.GetPropertyByMethod((MethodInfo)binding.Member), binding.Expression);
2487 }
2488
2489 return true;
2490 }
2491
2492 // .Select(p => everything else)
2493 //
2494 default :
2495 return false;
2496 }
2497 }
2498
2499 public void ReplaceParent(IBuildContext oldParent, IBuildContext newParent)
2500 {
2501 foreach (var context in Contexts)
2502 if (context != newParent)
2503 if (context.Parent == oldParent)
2504 context.Parent = newParent;
2505 }
2506
2507 #endregion
2508 }
2509 }