-
Notifications
You must be signed in to change notification settings - Fork 934
/
Copy pathExpressionProcessor.cs
907 lines (770 loc) · 38.8 KB
/
ExpressionProcessor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using NHibernate.Criterion;
using NHibernate.Dialect.Function;
using NHibernate.Engine;
using NHibernate.Type;
using NHibernate.Util;
using Expression = System.Linq.Expressions.Expression;
namespace NHibernate.Impl
{
/// <summary>
/// Subquery type enumeration
/// </summary>
public enum LambdaSubqueryType
{
/// <summary>exact</summary>
Exact = 1,
/// <summary>all</summary>
All = 2,
/// <summary>some</summary>
Some = 3,
}
/// <summary>
/// Converts lambda expressions to NHibernate criterion/order
/// </summary>
public static class ExpressionProcessor
{
public class ProjectionInfo
{
private string _property;
private IProjection _projection;
protected ProjectionInfo() { }
public static ProjectionInfo ForProperty(string property) { return new ProjectionInfo() { _property = property }; }
public static ProjectionInfo ForProjection(IProjection projection) { return new ProjectionInfo() { _projection = projection }; }
public IProjection AsProjection() { return _projection ?? Projections.Property(_property); }
public ICriterion CreateCriterion(Func<string, ICriterion> stringFunc, Func<IProjection, ICriterion> projectionFunc)
{
return (_property != null)
? stringFunc(_property)
: projectionFunc(_projection);
}
public ICriterion CreateCriterion(Func<string, object, ICriterion> stringFunc, Func<IProjection, object, ICriterion> projectionFunc, object value)
{
return (_property != null)
? stringFunc(_property, value)
: projectionFunc(_projection, value);
}
public ICriterion CreateCriterion(ProjectionInfo rhs,
Func<string, string, ICriterion> ssFunc,
Func<string, IProjection, ICriterion> spFunc,
Func<IProjection, string, ICriterion> psFunc,
Func<IProjection, IProjection, ICriterion> ppFunc)
{
if (_property != null && rhs._property != null)
return ssFunc(_property, rhs._property);
if (_property != null)
return spFunc(_property, rhs._projection);
if (rhs._property != null)
return psFunc(_projection, rhs._property);
return ppFunc(_projection, rhs._projection);
}
public T Create<T>(Func<string, T> stringFunc, Func<IProjection, T> projectionFunc)
{
return (_property != null)
? stringFunc(_property)
: projectionFunc(_projection);
}
public Order CreateOrder(Func<string, Order> orderStringDelegate, Func<IProjection, Order> orderProjectionDelegate)
{
return (_property != null)
? orderStringDelegate(_property)
: orderProjectionDelegate(_projection);
}
/// <summary>
/// Retrieve the property name from a supplied PropertyProjection
/// Note: throws if the supplied IProjection is not a IPropertyProjection
/// </summary>
public string AsProperty()
{
if (_property != null) return _property;
var propertyProjection = _projection as IPropertyProjection;
if (propertyProjection == null) throw new InvalidOperationException("Cannot determine property for " + _projection);
return propertyProjection.PropertyName;
}
internal bool IsConstant(out ConstantProjection value) => (value = _projection as ConstantProjection) != null;
}
private static readonly Dictionary<ExpressionType, Func<ProjectionInfo, object, ICriterion>> _simpleExpressionCreators;
private static readonly Dictionary<ExpressionType, Func<ProjectionInfo, ProjectionInfo, ICriterion>> _propertyExpressionCreators;
private static readonly Dictionary<LambdaSubqueryType, IDictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>> _subqueryExpressionCreatorTypes;
private static readonly Dictionary<string, Func<MethodCallExpression, ICriterion>> _customMethodCallProcessors;
private static readonly Dictionary<string, Func<Expression, IProjection>> _customProjectionProcessors;
private static readonly Dictionary<ExpressionType, ISQLFunction> _binaryArithmethicTemplates = new Dictionary<ExpressionType, ISQLFunction>();
private static readonly ISQLFunction _unaryNegateTemplate;
static ExpressionProcessor()
{
_simpleExpressionCreators = new Dictionary<ExpressionType, Func<ProjectionInfo, object, ICriterion>>();
_simpleExpressionCreators[ExpressionType.Equal] = Eq;
_simpleExpressionCreators[ExpressionType.NotEqual] = Ne;
_simpleExpressionCreators[ExpressionType.GreaterThan] = Gt;
_simpleExpressionCreators[ExpressionType.GreaterThanOrEqual] = Ge;
_simpleExpressionCreators[ExpressionType.LessThan] = Lt;
_simpleExpressionCreators[ExpressionType.LessThanOrEqual] = Le;
_propertyExpressionCreators = new Dictionary<ExpressionType, Func<ProjectionInfo, ProjectionInfo, ICriterion>>();
_propertyExpressionCreators[ExpressionType.Equal] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.EqProperty, Restrictions.EqProperty, Restrictions.EqProperty, Restrictions.EqProperty);
_propertyExpressionCreators[ExpressionType.NotEqual] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.NotEqProperty, Restrictions.NotEqProperty, Restrictions.NotEqProperty, Restrictions.NotEqProperty);
_propertyExpressionCreators[ExpressionType.GreaterThan] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.GtProperty, Restrictions.GtProperty, Restrictions.GtProperty, Restrictions.GtProperty);
_propertyExpressionCreators[ExpressionType.GreaterThanOrEqual] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.GeProperty, Restrictions.GeProperty, Restrictions.GeProperty, Restrictions.GeProperty);
_propertyExpressionCreators[ExpressionType.LessThan] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.LtProperty, Restrictions.LtProperty, Restrictions.LtProperty, Restrictions.LtProperty);
_propertyExpressionCreators[ExpressionType.LessThanOrEqual] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.LeProperty, Restrictions.LeProperty, Restrictions.LeProperty, Restrictions.LeProperty);
_subqueryExpressionCreatorTypes = new Dictionary<LambdaSubqueryType, IDictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>>();
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact] = new Dictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>();
_subqueryExpressionCreatorTypes[LambdaSubqueryType.All] = new Dictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>();
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Some] = new Dictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>();
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.Equal] = Subqueries.PropertyEq;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.NotEqual] = Subqueries.PropertyNe;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.GreaterThan] = Subqueries.PropertyGt;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.GreaterThanOrEqual] = Subqueries.PropertyGe;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.LessThan] = Subqueries.PropertyLt;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.LessThanOrEqual] = Subqueries.PropertyLe;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.Equal] = Subqueries.PropertyEqAll;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.GreaterThan] = Subqueries.PropertyGtAll;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.GreaterThanOrEqual] = Subqueries.PropertyGeAll;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.LessThan] = Subqueries.PropertyLtAll;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.LessThanOrEqual] = Subqueries.PropertyLeAll;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.GreaterThan] = Subqueries.PropertyGtSome;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.GreaterThanOrEqual] = Subqueries.PropertyGeSome;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.LessThan] = Subqueries.PropertyLtSome;
_subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.LessThanOrEqual] = Subqueries.PropertyLeSome;
_customMethodCallProcessors = new Dictionary<string, Func<MethodCallExpression, ICriterion>>();
RegisterCustomMethodCall(() => RestrictionExtensions.IsLike("", ""), RestrictionExtensions.ProcessIsLike);
RegisterCustomMethodCall(() => RestrictionExtensions.IsLike("", "", null), RestrictionExtensions.ProcessIsLikeMatchMode);
RegisterCustomMethodCall(() => RestrictionExtensions.IsLike("", "", null, null), RestrictionExtensions.ProcessIsLikeMatchModeEscapeChar);
RegisterCustomMethodCall(() => RestrictionExtensions.IsInsensitiveLike("", ""), RestrictionExtensions.ProcessIsInsensitiveLike);
RegisterCustomMethodCall(() => RestrictionExtensions.IsInsensitiveLike("", "", null), RestrictionExtensions.ProcessIsInsensitiveLikeMatchMode);
RegisterCustomMethodCall(() => RestrictionExtensions.IsIn(null, Array.Empty<object>()), RestrictionExtensions.ProcessIsInArray);
RegisterCustomMethodCall(() => RestrictionExtensions.IsIn(null, new List<object>()), RestrictionExtensions.ProcessIsInCollection);
RegisterCustomMethodCall(() => RestrictionExtensions.IsBetween(null, null).And(null), RestrictionExtensions.ProcessIsBetween);
_customProjectionProcessors = new Dictionary<string, Func<Expression, IProjection>>();
RegisterCustomProjection(() => default(DateTime).Year, e => ProjectionsExtensions.ProcessYear(e.Expression));
RegisterCustomProjection(() => default(DateTime).Day, e => ProjectionsExtensions.ProcessDay(e.Expression));
RegisterCustomProjection(() => default(DateTime).Month, e => ProjectionsExtensions.ProcessMonth(e.Expression));
RegisterCustomProjection(() => default(DateTime).Hour, e => ProjectionsExtensions.ProcessHour(e.Expression));
RegisterCustomProjection(() => default(DateTime).Minute, e => ProjectionsExtensions.ProcessMinute(e.Expression));
RegisterCustomProjection(() => default(DateTime).Second, e => ProjectionsExtensions.ProcessSecond(e.Expression));
RegisterCustomProjection(() => default(DateTime).Date, e => ProjectionsExtensions.ProcessDate(e.Expression));
RegisterCustomProjection(() => default(DateTimeOffset).Year, e => ProjectionsExtensions.ProcessYear(e.Expression));
RegisterCustomProjection(() => default(DateTimeOffset).Day, e => ProjectionsExtensions.ProcessDay(e.Expression));
RegisterCustomProjection(() => default(DateTimeOffset).Month, e => ProjectionsExtensions.ProcessMonth(e.Expression));
RegisterCustomProjection(() => default(DateTimeOffset).Hour, e => ProjectionsExtensions.ProcessHour(e.Expression));
RegisterCustomProjection(() => default(DateTimeOffset).Minute, e => ProjectionsExtensions.ProcessMinute(e.Expression));
RegisterCustomProjection(() => default(DateTimeOffset).Second, e => ProjectionsExtensions.ProcessSecond(e.Expression));
RegisterCustomProjection(() => default(DateTimeOffset).Date, e => ProjectionsExtensions.ProcessDate(e.Expression));
RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(int)), ProjectionsExtensions.ProcessSqrt);
RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(double)), ProjectionsExtensions.ProcessSqrt);
RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(decimal)), ProjectionsExtensions.ProcessSqrt);
RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(byte)), ProjectionsExtensions.ProcessSqrt);
RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(long)), ProjectionsExtensions.ProcessSqrt);
RegisterCustomProjection(() => ProjectionsExtensions.Lower(string.Empty), ProjectionsExtensions.ProcessLower);
RegisterCustomProjection(() => ProjectionsExtensions.Upper(string.Empty), ProjectionsExtensions.ProcessUpper);
RegisterCustomProjection(() => ProjectionsExtensions.TrimStr(string.Empty), ProjectionsExtensions.ProcessTrimStr);
RegisterCustomProjection(() => ProjectionsExtensions.StrLength(string.Empty), ProjectionsExtensions.ProcessStrLength);
RegisterCustomProjection(() => ProjectionsExtensions.BitLength(string.Empty), ProjectionsExtensions.ProcessBitLength);
RegisterCustomProjection(() => ProjectionsExtensions.Substr(string.Empty, 0, 0), ProjectionsExtensions.ProcessSubstr);
RegisterCustomProjection(() => ProjectionsExtensions.CharIndex(string.Empty, string.Empty, 0), ProjectionsExtensions.ProcessCharIndex);
RegisterCustomProjection(() => ProjectionsExtensions.Coalesce<DBNull>(null, null), ProjectionsExtensions.ProcessCoalesce);
RegisterCustomProjection(() => ProjectionsExtensions.Coalesce<int>(null, 0), ProjectionsExtensions.ProcessCoalesce);
RegisterCustomProjection(() => Projections.Concat(null), Projections.ProcessConcat);
RegisterCustomProjection(() => ProjectionsExtensions.Mod(0, 0), ProjectionsExtensions.ProcessMod);
RegisterCustomProjection(() => ProjectionsExtensions.Abs(default(int)), ProjectionsExtensions.ProcessIntAbs);
RegisterCustomProjection(() => ProjectionsExtensions.Abs(default(double)), ProjectionsExtensions.ProcessDoubleAbs);
RegisterCustomProjection(() => ProjectionsExtensions.Abs(default(Int64)), ProjectionsExtensions.ProcessInt64Abs);
RegisterCustomProjection(() => Math.Round(default(double)), ProjectionsExtensions.ProcessRound);
RegisterCustomProjection(() => Math.Round(default(decimal)), ProjectionsExtensions.ProcessRound);
RegisterCustomProjection(() => Math.Round(default(double), default(int)), ProjectionsExtensions.ProcessRound);
RegisterCustomProjection(() => Math.Round(default(decimal), default(int)), ProjectionsExtensions.ProcessRound);
RegisterCustomProjection(() => ProjectionsExtensions.AsEntity(default(object)), ProjectionsExtensions.ProcessAsEntity);
RegisterBinaryArithmeticExpression(ExpressionType.Add, "+");
RegisterBinaryArithmeticExpression(ExpressionType.Subtract, "-");
RegisterBinaryArithmeticExpression(ExpressionType.Multiply, "*");
RegisterBinaryArithmeticExpression(ExpressionType.Divide, "/");
_unaryNegateTemplate = new VarArgsSQLFunction("(-", string.Empty, ")");
}
private static void RegisterBinaryArithmeticExpression(ExpressionType type, string sqlOperand)
{
_binaryArithmethicTemplates[type] = new VarArgsSQLFunction("(", sqlOperand, ")");
}
private static ICriterion Eq(ProjectionInfo property, object value)
{
return property.CreateCriterion(Restrictions.Eq, Restrictions.Eq, value);
}
private static ICriterion Ne(ProjectionInfo property, object value)
{
return
Restrictions.Not(
property.CreateCriterion(Restrictions.Eq, Restrictions.Eq, value));
}
private static ICriterion Gt(ProjectionInfo property, object value)
{
return property.CreateCriterion(Restrictions.Gt, Restrictions.Gt, value);
}
private static ICriterion Ge(ProjectionInfo property, object value)
{
return property.CreateCriterion(Restrictions.Ge, Restrictions.Ge, value);
}
private static ICriterion Lt(ProjectionInfo property, object value)
{
return property.CreateCriterion(Restrictions.Lt, Restrictions.Lt, value);
}
private static ICriterion Le(ProjectionInfo property, object value)
{
return property.CreateCriterion(Restrictions.Le, Restrictions.Le, value);
}
/// <summary>
/// Invoke the expression to extract its runtime value
/// </summary>
public static object FindValue(Expression expression)
{
var valueExpression = Expression.Lambda(expression).Compile();
object value = valueExpression.DynamicInvoke();
return value;
}
/// <summary>
/// Retrieves the projection for the expression
/// </summary>
public static ProjectionInfo FindMemberProjection(Expression expression)
{
if (!IsMemberExpression(expression))
return AsArithmeticProjection(expression)
?? ProjectionInfo.ForProjection(Projections.Constant(FindValue(expression), NHibernateUtil.GuessType(expression.Type)));
var unwrapExpression = UnwrapConvertExpression(expression);
if (unwrapExpression != null)
{
return FindMemberProjection(unwrapExpression);
}
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression != null)
{
var signature = Signature(methodCallExpression.Method);
Func<Expression, IProjection> processor;
if (_customProjectionProcessors.TryGetValue(signature, out processor))
{
return ProjectionInfo.ForProjection(processor(methodCallExpression));
}
}
var memberExpression = expression as MemberExpression;
if (memberExpression != null)
{
var signature = Signature(memberExpression.Member);
Func<Expression, IProjection> processor;
if (_customProjectionProcessors.TryGetValue(signature, out processor))
{
return ProjectionInfo.ForProjection(processor(memberExpression));
}
}
return ProjectionInfo.ForProperty(FindMemberExpression(expression));
}
private static Expression UnwrapConvertExpression(Expression expression)
{
if (expression is UnaryExpression unaryExpression)
{
if (!IsConversion(unaryExpression.NodeType))
{
if (IsSupportedUnaryExpression(unaryExpression))
return null;
throw new ArgumentException("Cannot interpret member from " + expression, nameof(expression));
}
return unaryExpression.Operand;
}
return null;
}
private static bool IsSupportedUnaryExpression(UnaryExpression expression)
{
return expression.NodeType == ExpressionType.Negate;
}
private static ProjectionInfo AsArithmeticProjection(Expression expression)
{
if (!(expression is BinaryExpression be))
{
if (expression is UnaryExpression unary && unary.NodeType == ExpressionType.Negate)
{
return ProjectionInfo.ForProjection(
new SqlFunctionProjection(_unaryNegateTemplate, TypeFactory.HeuristicType(unary.Type), FindMemberProjection(unary.Operand).AsProjection()));
}
var unwrapExpression = UnwrapConvertExpression(expression);
return unwrapExpression != null ? AsArithmeticProjection(unwrapExpression) : null;
}
if (!_binaryArithmethicTemplates.TryGetValue(be.NodeType, out var template))
{
return null;
}
return ProjectionInfo.ForProjection(
new SqlFunctionProjection(
template,
TypeFactory.HeuristicType(be.Type),
FindMemberProjection(be.Left).AsProjection(),
FindMemberProjection(be.Right).AsProjection()));
}
//http://stackoverflow.com/a/2509524/259946
private static readonly Regex GeneratedMemberNameRegex = new Regex(@"^(CS\$)?<\w*>[1-9a-s]__[a-zA-Z]+[0-9]*$", RegexOptions.Compiled | RegexOptions.Singleline);
private static bool IsCompilerGeneratedMemberExpressionOfCompilerGeneratedClass(Expression expression)
{
var memberExpression = expression as MemberExpression;
if (memberExpression != null && memberExpression.Member.DeclaringType != null)
{
return Attribute.GetCustomAttribute(memberExpression.Member.DeclaringType, typeof(CompilerGeneratedAttribute)) != null
&& GeneratedMemberNameRegex.IsMatch(memberExpression.Member.Name);
}
return false;
}
/// <summary>
/// Retrieves the name of the property from a member expression
/// </summary>
/// <param name="expression">An expression tree that can contain either a member, or a conversion from a member.
/// If the member is referenced from a null valued object, then the container is treated as an alias.</param>
/// <returns>The name of the member property</returns>
public static string FindMemberExpression(Expression expression)
{
var memberExpression = expression as MemberExpression;
if (memberExpression != null)
{
var parentExpression = memberExpression.Expression;
if (parentExpression != null)
{
if (parentExpression.NodeType == ExpressionType.MemberAccess
|| parentExpression.NodeType == ExpressionType.Call)
{
if (memberExpression.Member.DeclaringType.IsNullable())
{
// it's a Nullable<T>, so ignore any .Value
if (memberExpression.Member.Name == "Value")
return FindMemberExpression(parentExpression);
}
if (IsCompilerGeneratedMemberExpressionOfCompilerGeneratedClass(parentExpression))
{
return memberExpression.Member.Name;
}
return FindMemberExpression(parentExpression) + "." + memberExpression.Member.Name;
}
if (IsConversion(parentExpression.NodeType))
{
return (FindMemberExpression(parentExpression) + "." + memberExpression.Member.Name).TrimStart('.');
}
}
return memberExpression.Member.Name;
}
var unaryExpression = expression as UnaryExpression;
if (unaryExpression != null)
{
if (!IsConversion(unaryExpression.NodeType))
throw new ArgumentException("Cannot interpret member from " + expression, nameof(expression));
return FindMemberExpression(unaryExpression.Operand);
}
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression != null)
{
if (methodCallExpression.Method.Name == "GetType")
return ClassMember(methodCallExpression.Object);
if (methodCallExpression.Method.Name == "get_Item")
return FindMemberExpression(methodCallExpression.Object);
if (methodCallExpression.Method.Name == "First")
return FindMemberExpression(methodCallExpression.Arguments[0]);
throw new ArgumentException("Unrecognised method call in expression " + methodCallExpression, nameof(expression));
}
if (expression is ParameterExpression)
return "";
throw new ArgumentException("Could not determine member from " + expression, nameof(expression));
}
/// <summary>
/// Retrieves the name of the property from a member expression (without leading member access)
/// </summary>
public static string FindPropertyExpression(Expression expression)
{
string memberExpression = FindMemberExpression(expression);
int periodPosition = memberExpression.LastIndexOf('.') + 1;
string property = (periodPosition <= 0) ? memberExpression : memberExpression.Substring(periodPosition);
return property;
}
/// <summary>
/// Retrieves a detached criteria from an appropriate lambda expression
/// </summary>
/// <param name="expression">Expression for detached criteria using .As<>() extension"/></param>
/// <returns>Evaluated detached criteria</returns>
public static DetachedCriteria FindDetachedCriteria(Expression expression)
{
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression == null)
throw new ArgumentException("right operand should be detachedQueryInstance.As<T>() - " + expression, nameof(expression));
var criteriaExpression = Expression.Lambda(methodCallExpression.Object).Compile();
QueryOver detachedQuery = (QueryOver)criteriaExpression.DynamicInvoke();
return detachedQuery.DetachedCriteria;
}
private static bool EvaluatesToNull(Expression expression)
{
var valueExpression = Expression.Lambda(expression).Compile();
object value = valueExpression.DynamicInvoke();
return (value == null);
}
private static System.Type FindMemberType(Expression expression)
{
var memberExpression = expression as MemberExpression;
if (memberExpression != null)
{
return memberExpression.Type;
}
var unwrapExpression = UnwrapConvertExpression(expression);
if (unwrapExpression != null)
{
return FindMemberType(unwrapExpression);
}
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression != null)
{
return methodCallExpression.Method.ReturnType;
}
if (expression is BinaryExpression || expression is UnaryExpression)
return expression.Type;
throw new ArgumentException("Could not determine member type from " + expression, nameof(expression));
}
private static bool IsMemberExpression(Expression expression)
{
if (expression is ParameterExpression)
return true;
var memberExpression = expression as MemberExpression;
if (memberExpression != null)
{
if (memberExpression.Expression == null)
return false; // it's a member of a static class
if (IsMemberExpression(memberExpression.Expression))
return true;
// if the member has a null value, it was an alias
return EvaluatesToNull(memberExpression.Expression);
}
var unwrapExpression = UnwrapConvertExpression(expression);
if (unwrapExpression != null)
{
return IsMemberExpression(unwrapExpression);
}
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression != null)
{
string signature = Signature(methodCallExpression.Method);
if (_customProjectionProcessors.ContainsKey(signature))
return true;
if (methodCallExpression.Method.Name == "First")
{
if (IsMemberExpression(methodCallExpression.Arguments[0]))
return true;
return EvaluatesToNull(methodCallExpression.Arguments[0]);
}
if (methodCallExpression.Method.Name == "GetType"
|| methodCallExpression.Method.Name == "get_Item")
{
if (IsMemberExpression(methodCallExpression.Object))
return true;
return EvaluatesToNull(methodCallExpression.Object);
}
}
return false;
}
private static bool IsConversion(ExpressionType expressionType)
{
return (expressionType == ExpressionType.Convert || expressionType == ExpressionType.ConvertChecked);
}
private static object ConvertType(object value, System.Type type)
{
if (value == null)
return null;
if (type.IsInstanceOfType(value))
return value;
type = type.UnwrapIfNullable();
if (type.IsEnum)
return Enum.ToObject(type, value);
if (type.IsPrimitive)
return Convert.ChangeType(value, type);
throw new ArgumentException(string.Format("Cannot convert '{0}' to {1}", value, type));
}
private static ICriterion ProcessSimpleExpression(Expression left, TypedValue rightValue, ExpressionType nodeType)
{
ProjectionInfo property = FindMemberProjection(left);
System.Type propertyType = FindMemberType(left);
var value = ConvertType(rightValue.Value, propertyType);
if (value == null)
return ProcessSimpleNullExpression(property, nodeType);
Func<ProjectionInfo, object, ICriterion> simpleExpressionCreator;
if (!_simpleExpressionCreators.TryGetValue(nodeType, out simpleExpressionCreator))
throw new InvalidOperationException("Unhandled simple expression type: " + nodeType);
return simpleExpressionCreator(property, value);
}
private static ICriterion ProcessAsVisualBasicStringComparison(Expression left, ExpressionType nodeType)
{
if (left.NodeType != ExpressionType.Call)
{
return null;
}
var methodCall = (MethodCallExpression) left;
return methodCall.Method.Name == "CompareString"
? ProcessMemberExpression(methodCall.Arguments[0], methodCall.Arguments[1], nodeType)
: null;
}
private static ICriterion ProcessSimpleNullExpression(ProjectionInfo property, ExpressionType expressionType)
{
if (expressionType == ExpressionType.Equal)
return property.CreateCriterion(Restrictions.IsNull, Restrictions.IsNull);
if (expressionType == ExpressionType.NotEqual)
return Restrictions.Not(
property.CreateCriterion(Restrictions.IsNull, Restrictions.IsNull));
throw new ArgumentException("Cannot supply null value to operator " + expressionType, nameof(expressionType));
}
private static ICriterion ProcessMemberExpression(Expression left, Expression right, ExpressionType nodeType)
{
ProjectionInfo rightProperty = FindMemberProjection(right);
if (rightProperty.IsConstant(out var constProjection))
{
return ProcessAsVisualBasicStringComparison(left, nodeType)
?? ProcessSimpleExpression(left, constProjection.TypedValue, nodeType);
}
ProjectionInfo leftProperty = FindMemberProjection(left);
Func<ProjectionInfo, ProjectionInfo, ICriterion> propertyExpressionCreator;
if (!_propertyExpressionCreators.TryGetValue(nodeType, out propertyExpressionCreator))
throw new InvalidOperationException("Unhandled property expression type: " + nodeType);
return propertyExpressionCreator(leftProperty, rightProperty);
}
private static ICriterion ProcessAndExpression(BinaryExpression expression)
{
return Restrictions.And(
ProcessExpression(expression.Left),
ProcessExpression(expression.Right));
}
private static ICriterion ProcessOrExpression(BinaryExpression expression)
{
return Restrictions.Or(
ProcessExpression(expression.Left),
ProcessExpression(expression.Right));
}
private static ICriterion ProcessBinaryExpression(BinaryExpression expression)
{
switch (expression.NodeType)
{
case ExpressionType.AndAlso:
return ProcessAndExpression(expression);
case ExpressionType.OrElse:
return ProcessOrExpression(expression);
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
return ProcessMemberExpression(expression.Left, expression.Right, expression.NodeType);
default:
throw new NotImplementedException("Unhandled binary expression: " + expression.NodeType + ", " + expression);
}
}
private static ICriterion ProcessBooleanExpression(Expression expression)
{
if (expression is MemberExpression)
{
return Restrictions.Eq(FindMemberExpression(expression), true);
}
var unaryExpression = expression as UnaryExpression;
if (unaryExpression != null)
{
if (unaryExpression.NodeType != ExpressionType.Not)
throw new ArgumentException("Cannot interpret member from " + expression, nameof(expression));
if (IsMemberExpression(unaryExpression.Operand))
return Restrictions.Eq(FindMemberExpression(unaryExpression.Operand), false);
else
return Restrictions.Not(ProcessExpression(unaryExpression.Operand));
}
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression != null)
{
return ProcessCustomMethodCall(methodCallExpression);
}
var typeBinaryExpression = expression as TypeBinaryExpression;
if (typeBinaryExpression != null)
{
return Restrictions.Eq(ClassMember(typeBinaryExpression.Expression), typeBinaryExpression.TypeOperand.FullName);
}
throw new ArgumentException(
"Could not determine member type from " + expression.NodeType + ", " + expression + ", " + expression.GetType(),
nameof(expression));
}
private static string ClassMember(Expression expression)
{
if (expression.NodeType == ExpressionType.MemberAccess)
return FindMemberExpression(expression) + ".class";
return "class";
}
public static string Signature(MethodInfo methodInfo)
{
while (methodInfo.IsGenericMethod && !methodInfo.IsGenericMethodDefinition)
methodInfo = methodInfo.GetGenericMethodDefinition();
return methodInfo.DeclaringType.FullName
+ ":" + methodInfo;
}
public static string Signature(MemberInfo memberInfo)
{
return memberInfo.DeclaringType.FullName + ":" + memberInfo;
}
private static ICriterion ProcessCustomMethodCall(MethodCallExpression methodCallExpression)
{
string signature = Signature(methodCallExpression.Method);
Func<MethodCallExpression, ICriterion> customMethodCallProcessor;
if (!_customMethodCallProcessors.TryGetValue(signature, out customMethodCallProcessor))
throw new InvalidOperationException("Unrecognised method call: " + signature);
return customMethodCallProcessor(methodCallExpression);
}
private static ICriterion ProcessExpression(Expression expression)
{
var binaryExpression = expression as BinaryExpression;
if (binaryExpression != null)
return ProcessBinaryExpression(binaryExpression);
return ProcessBooleanExpression(expression);
}
private static ICriterion ProcessLambdaExpression(LambdaExpression expression)
{
return ProcessExpression(expression.Body);
}
/// <summary>
/// Convert a lambda expression to NHibernate ICriterion
/// </summary>
/// <typeparam name="T">The type of the lambda expression</typeparam>
/// <param name="expression">The lambda expression to convert</param>
/// <returns>NHibernate ICriterion</returns>
public static ICriterion ProcessExpression<T>(Expression<Func<T, bool>> expression)
{
return ProcessLambdaExpression(expression);
}
/// <summary>
/// Convert a lambda expression to NHibernate ICriterion
/// </summary>
/// <param name="expression">The lambda expression to convert</param>
/// <returns>NHibernate ICriterion</returns>
public static ICriterion ProcessExpression(Expression<Func<bool>> expression)
{
return ProcessLambdaExpression(expression);
}
/// <summary>
/// Convert a lambda expression to NHibernate Order
/// </summary>
/// <typeparam name="T">The type of the lambda expression</typeparam>
/// <param name="expression">The lambda expression to convert</param>
/// <param name="orderDelegate">The appropriate order delegate (order direction)</param>
/// <returns>NHibernate Order</returns>
public static Order ProcessOrder<T>(Expression<Func<T, object>> expression,
Func<string, Order> orderDelegate)
{
string property = FindMemberExpression(expression.Body);
Order order = orderDelegate(property);
return order;
}
/// <summary>
/// Convert a lambda expression to NHibernate Order
/// </summary>
/// <param name="expression">The lambda expression to convert</param>
/// <param name="orderDelegate">The appropriate order delegate (order direction)</param>
/// <returns>NHibernate Order</returns>
public static Order ProcessOrder(Expression<Func<object>> expression,
Func<string, Order> orderDelegate)
{
string property = FindMemberExpression(expression.Body);
Order order = orderDelegate(property);
return order;
}
/// <summary>
/// Convert a lambda expression to NHibernate Order
/// </summary>
/// <param name="expression">The lambda expression to convert</param>
/// <param name="orderDelegate">The appropriate order delegate (order direction)</param>
/// <returns>NHibernate Order</returns>
public static Order ProcessOrder( LambdaExpression expression,
Func<string, Order> orderDelegate)
{
string property = FindPropertyExpression(expression.Body);
Order order = orderDelegate(property);
return order;
}
/// <summary>
/// Convert a lambda expression to NHibernate Order
/// </summary>
/// <param name="expression">The lambda expression to convert</param>
/// <param name="orderStringDelegate">The appropriate order delegate (order direction)</param>
/// <param name="orderProjectionDelegate">The appropriate order delegate (order direction)</param>
/// <returns>NHibernate Order</returns>
public static Order ProcessOrder( LambdaExpression expression,
Func<string, Order> orderStringDelegate,
Func<IProjection, Order> orderProjectionDelegate)
{
ProjectionInfo projection = FindMemberProjection(expression.Body);
Order order = projection.CreateOrder(orderStringDelegate, orderProjectionDelegate);
return order;
}
private static AbstractCriterion ProcessSubqueryExpression(LambdaSubqueryType subqueryType,
BinaryExpression be)
{
string property = FindMemberExpression(be.Left);
DetachedCriteria detachedCriteria = FindDetachedCriteria(be.Right);
var subqueryExpressionCreators = _subqueryExpressionCreatorTypes[subqueryType];
Func<string, DetachedCriteria, AbstractCriterion> subqueryExpressionCreator;
if (!subqueryExpressionCreators.TryGetValue(be.NodeType, out subqueryExpressionCreator))
throw new InvalidOperationException("Unhandled subquery expression type: " + subqueryType + "," + be.NodeType);
return subqueryExpressionCreator(property, detachedCriteria);
}
/// <summary>
/// Convert a lambda expression to NHibernate subquery AbstractCriterion
/// </summary>
/// <typeparam name="T">type of member expression</typeparam>
/// <param name="subqueryType">type of subquery</param>
/// <param name="expression">lambda expression to convert</param>
/// <returns>NHibernate.ICriterion.AbstractCriterion</returns>
public static AbstractCriterion ProcessSubquery<T>(LambdaSubqueryType subqueryType,
Expression<Func<T, bool>> expression)
{
BinaryExpression be = (BinaryExpression)expression.Body;
AbstractCriterion criterion = ProcessSubqueryExpression(subqueryType, be);
return criterion;
}
/// <summary>
/// Convert a lambda expression to NHibernate subquery AbstractCriterion
/// </summary>
/// <param name="subqueryType">type of subquery</param>
/// <param name="expression">lambda expression to convert</param>
/// <returns>NHibernate.ICriterion.AbstractCriterion</returns>
public static AbstractCriterion ProcessSubquery(LambdaSubqueryType subqueryType,
Expression<Func<bool>> expression)
{
BinaryExpression be = (BinaryExpression)expression.Body;
AbstractCriterion criterion = ProcessSubqueryExpression(subqueryType, be);
return criterion;
}
/// <summary>
/// Register a custom method for use in a QueryOver expression
/// </summary>
/// <param name="function">Lambda expression demonstrating call of custom method</param>
/// <param name="functionProcessor">function to convert MethodCallExpression to ICriterion</param>
public static void RegisterCustomMethodCall(Expression<Func<bool>> function, Func<MethodCallExpression, ICriterion> functionProcessor)
{
MethodCallExpression functionExpression = (MethodCallExpression)function.Body;
string signature = Signature(functionExpression.Method);
_customMethodCallProcessors.Add(signature, functionProcessor);
}
/// <summary>
/// Register a custom projection for use in a QueryOver expression
/// </summary>
/// <param name="function">Lambda expression demonstrating call of custom method</param>
/// <param name="functionProcessor">function to convert MethodCallExpression to IProjection</param>
public static void RegisterCustomProjection<T>(Expression<Func<T>> function, Func<MethodCallExpression, IProjection> functionProcessor)
{
MethodCallExpression functionExpression = (MethodCallExpression)function.Body;
string signature = Signature(functionExpression.Method);
_customProjectionProcessors.Add(signature, e => functionProcessor((MethodCallExpression) e));
}
/// <summary>
/// Register a custom projection for use in a QueryOver expression
/// </summary>
/// <param name="function">Lambda expression demonstrating call of custom method</param>
/// <param name="functionProcessor">function to convert MethodCallExpression to IProjection</param>
public static void RegisterCustomProjection<T>(Expression<Func<T>> function, Func<MemberExpression, IProjection> functionProcessor)
{
MemberExpression functionExpression = (MemberExpression) function.Body;
string signature = Signature(functionExpression.Member);
_customProjectionProcessors.Add(signature, e => functionProcessor((MemberExpression) e));
}
}
}