1
1
using System . Collections . Generic ;
2
2
using System . Collections ;
3
+ using System . Globalization ;
4
+ using System . Linq . Dynamic . Core . Exceptions ;
3
5
#if ! ( WINDOWS_APP45x || SILVERLIGHT )
4
6
using System . Diagnostics ;
5
7
#endif
@@ -46,7 +48,7 @@ private static Expression OptimizeExpression(Expression expression)
46
48
return expression ;
47
49
}
48
50
49
- #region Any
51
+ #region Any
50
52
private static readonly MethodInfo _any = GetMethod ( nameof ( Queryable . Any ) ) ;
51
53
52
54
/// <summary>
@@ -94,9 +96,9 @@ public static bool Any([NotNull] this IQueryable source, [NotNull] string predic
94
96
95
97
return Execute < bool > ( _anyPredicate , source , lambda ) ;
96
98
}
97
- #endregion Any
99
+ #endregion Any
98
100
99
- #region AsEnumerable
101
+ #region AsEnumerable
100
102
#if NET35
101
103
/// <summary>
102
104
/// Returns the input typed as <see cref="IEnumerable{T}"/> of <see cref="object"/>./>
@@ -118,9 +120,9 @@ public static IEnumerable<dynamic> AsEnumerable([NotNull] this IQueryable source
118
120
yield return obj ;
119
121
}
120
122
}
121
- #endregion AsEnumerable
123
+ #endregion AsEnumerable
122
124
123
- #region Count
125
+ #region Count
124
126
private static readonly MethodInfo _count = GetMethod ( nameof ( Queryable . Count ) ) ;
125
127
126
128
/// <summary>
@@ -168,9 +170,9 @@ public static int Count([NotNull] this IQueryable source, [NotNull] string predi
168
170
169
171
return Execute < int > ( _countPredicate , source , lambda ) ;
170
172
}
171
- #endregion Count
173
+ #endregion Count
172
174
173
- #region Distinct
175
+ #region Distinct
174
176
private static readonly MethodInfo _distinct = GetMethod ( nameof ( Queryable . Distinct ) ) ;
175
177
176
178
/// <summary>
@@ -192,9 +194,9 @@ public static IQueryable Distinct([NotNull] this IQueryable source)
192
194
var optimized = OptimizeExpression ( Expression . Call ( typeof ( Queryable ) , "Distinct" , new Type [ ] { source . ElementType } , source . Expression ) ) ;
193
195
return source . Provider . CreateQuery ( optimized ) ;
194
196
}
195
- #endregion Distinct
197
+ #endregion Distinct
196
198
197
- #region First
199
+ #region First
198
200
private static readonly MethodInfo _first = GetMethod ( nameof ( Queryable . First ) ) ;
199
201
200
202
/// <summary>
@@ -236,9 +238,9 @@ public static dynamic First([NotNull] this IQueryable source, [NotNull] string p
236
238
237
239
return Execute ( _firstPredicate , source , lambda ) ;
238
240
}
239
- #endregion First
241
+ #endregion First
240
242
241
- #region FirstOrDefault
243
+ #region FirstOrDefault
242
244
/// <summary>
243
245
/// Returns the first element of a sequence, or a default value if the sequence contains no elements.
244
246
/// </summary>
@@ -278,9 +280,9 @@ public static dynamic FirstOrDefault([NotNull] this IQueryable source, [NotNull]
278
280
return Execute ( _firstOrDefaultPredicate , source , lambda ) ;
279
281
}
280
282
private static readonly MethodInfo _firstOrDefaultPredicate = GetMethod ( nameof ( Queryable . FirstOrDefault ) , 1 ) ;
281
- #endregion FirstOrDefault
283
+ #endregion FirstOrDefault
282
284
283
- #region GroupBy
285
+ #region GroupBy
284
286
/// <summary>
285
287
/// Groups the elements of a sequence according to a specified key string function
286
288
/// and creates a result value from each group and its key.
@@ -366,9 +368,9 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str
366
368
367
369
return source . Provider . CreateQuery ( optimized ) ;
368
370
}
369
- #endregion GroupBy
371
+ #endregion GroupBy
370
372
371
- #region GroupByMany
373
+ #region GroupByMany
372
374
/// <summary>
373
375
/// Groups the elements of a sequence according to multiple specified key string functions
374
376
/// and creates a result value from each group (and subgroups) and its key.
@@ -427,9 +429,9 @@ static IEnumerable<GroupResult> GroupByManyInternal<TElement>(IEnumerable<TEleme
427
429
428
430
return result ;
429
431
}
430
- #endregion GroupByMany
432
+ #endregion GroupByMany
431
433
432
- #region Join
434
+ #region Join
433
435
/// <summary>
434
436
/// Correlates the elements of two sequences based on matching keys. The default equality comparer is used to compare keys.
435
437
/// </summary>
@@ -450,20 +452,47 @@ public static IQueryable Join([NotNull] this IQueryable outer, [NotNull] IEnumer
450
452
Check . NotEmpty ( innerKeySelector , nameof ( innerKeySelector ) ) ;
451
453
Check . NotEmpty ( resultSelector , nameof ( resultSelector ) ) ;
452
454
455
+ Type outerType = outer . ElementType ;
456
+ Type innerType = inner . AsQueryable ( ) . ElementType ;
457
+
453
458
bool createParameterCtor = outer . IsLinqToObjects ( ) ;
454
- LambdaExpression outerSelectorLambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , outer . ElementType , null , outerKeySelector , args ) ;
455
- LambdaExpression innerSelectorLambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , inner . AsQueryable ( ) . ElementType , null , innerKeySelector , args ) ;
459
+ LambdaExpression outerSelectorLambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , outerType , null , outerKeySelector , args ) ;
460
+ LambdaExpression innerSelectorLambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , innerType , null , innerKeySelector , args ) ;
461
+
462
+ Type outerSelectorReturnType = outerSelectorLambda . Body . Type ;
463
+ Type innerSelectorReturnType = innerSelectorLambda . Body . Type ;
464
+
465
+ // If types are not the same, try to convert to Nullable and generate new LambdaExpression
466
+ if ( outerSelectorReturnType != innerSelectorReturnType )
467
+ {
468
+ if ( ExpressionParser . IsNullableType ( outerSelectorReturnType ) && ! ExpressionParser . IsNullableType ( innerSelectorReturnType ) )
469
+ {
470
+ innerSelectorReturnType = ExpressionParser . ToNullableType ( innerSelectorReturnType ) ;
471
+ innerSelectorLambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , innerType , innerSelectorReturnType , innerKeySelector , args ) ;
472
+ }
473
+ else if ( ! ExpressionParser . IsNullableType ( outerSelectorReturnType ) && ExpressionParser . IsNullableType ( innerSelectorReturnType ) )
474
+ {
475
+ outerSelectorReturnType = ExpressionParser . ToNullableType ( outerSelectorReturnType ) ;
476
+ outerSelectorLambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , outerType , outerSelectorReturnType , outerKeySelector , args ) ;
477
+ }
478
+
479
+ // If types are still not the same, throw an Exception
480
+ if ( outerSelectorReturnType != innerSelectorReturnType )
481
+ {
482
+ throw new ParseException ( string . Format ( CultureInfo . CurrentCulture , Res . IncompatibleTypes , outerType , innerType ) , - 1 ) ;
483
+ }
484
+ }
456
485
457
- ParameterExpression [ ] parameters = new [ ]
486
+ ParameterExpression [ ] parameters =
458
487
{
459
- Expression . Parameter ( outer . ElementType , "outer" ) , Expression . Parameter ( inner . AsQueryable ( ) . ElementType , "inner" )
488
+ Expression . Parameter ( outerType , "outer" ) , Expression . Parameter ( innerType , "inner" )
460
489
} ;
461
490
462
491
LambdaExpression resultSelectorLambda = DynamicExpressionParser . ParseLambda ( createParameterCtor , parameters , null , resultSelector , args ) ;
463
492
464
493
var optimized = OptimizeExpression ( Expression . Call (
465
494
typeof ( Queryable ) , "Join" ,
466
- new [ ] { outer . ElementType , inner . AsQueryable ( ) . ElementType , outerSelectorLambda . Body . Type , resultSelectorLambda . Body . Type } ,
495
+ new [ ] { outerType , innerType , outerSelectorLambda . Body . Type , resultSelectorLambda . Body . Type } ,
467
496
outer . Expression , // outer: The first sequence to join.
468
497
inner . AsQueryable ( ) . Expression , // inner: The sequence to join to the first sequence.
469
498
Expression . Quote ( outerSelectorLambda ) , // outerKeySelector: A function to extract the join key from each element of the first sequence.
@@ -490,9 +519,9 @@ public static IQueryable<TElement> Join<TElement>([NotNull] this IQueryable<TEle
490
519
{
491
520
return ( IQueryable < TElement > ) Join ( ( IQueryable ) outer , ( IEnumerable ) inner , outerKeySelector , innerKeySelector , resultSelector , args ) ;
492
521
}
493
- #endregion Join
522
+ #endregion Join
494
523
495
- #region Last
524
+ #region Last
496
525
private static readonly MethodInfo _last = GetMethod ( nameof ( Queryable . Last ) ) ;
497
526
/// <summary>
498
527
/// Returns the last element of a sequence.
@@ -509,9 +538,9 @@ public static dynamic Last([NotNull] this IQueryable source)
509
538
510
539
return Execute ( _last , source ) ;
511
540
}
512
- #endregion Last
541
+ #endregion Last
513
542
514
- #region LastOrDefault
543
+ #region LastOrDefault
515
544
private static readonly MethodInfo _lastDefault = GetMethod ( nameof ( Queryable . LastOrDefault ) ) ;
516
545
/// <summary>
517
546
/// Returns the last element of a sequence, or a default value if the sequence contains no elements.
@@ -528,9 +557,9 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source)
528
557
529
558
return Execute ( _lastDefault , source ) ;
530
559
}
531
- #endregion LastOrDefault
560
+ #endregion LastOrDefault
532
561
533
- #region OrderBy
562
+ #region OrderBy
534
563
/// <summary>
535
564
/// Sorts the elements of a sequence in ascending or descending order according to a key.
536
565
/// </summary>
@@ -589,9 +618,9 @@ public static IOrderedQueryable OrderBy([NotNull] this IQueryable source, [NotNu
589
618
var optimized = OptimizeExpression ( queryExpr ) ;
590
619
return ( IOrderedQueryable ) source . Provider . CreateQuery ( optimized ) ;
591
620
}
592
- #endregion OrderBy
621
+ #endregion OrderBy
593
622
594
- #region Page/PageResult
623
+ #region Page/PageResult
595
624
/// <summary>
596
625
/// Returns the elements as paged.
597
626
/// </summary>
@@ -677,9 +706,9 @@ public static PagedResult<TSource> PageResult<TSource>([NotNull] this IQueryable
677
706
678
707
return result ;
679
708
}
680
- #endregion Page/PageResult
709
+ #endregion Page/PageResult
681
710
682
- #region Reverse
711
+ #region Reverse
683
712
/// <summary>
684
713
/// Inverts the order of the elements in a sequence.
685
714
/// </summary>
@@ -691,9 +720,9 @@ public static IQueryable Reverse([NotNull] this IQueryable source)
691
720
692
721
return Queryable . Reverse ( ( IQueryable < object > ) source ) ;
693
722
}
694
- #endregion Reverse
723
+ #endregion Reverse
695
724
696
- #region Select
725
+ #region Select
697
726
/// <summary>
698
727
/// Projects each element of a sequence into a new form.
699
728
/// </summary>
@@ -786,9 +815,9 @@ public static IQueryable Select([NotNull] this IQueryable source, [NotNull] Type
786
815
787
816
return source . Provider . CreateQuery ( optimized ) ;
788
817
}
789
- #endregion Select
818
+ #endregion Select
790
819
791
- #region SelectMany
820
+ #region SelectMany
792
821
/// <summary>
793
822
/// Projects each element of a sequence to an <see cref="IQueryable"/> and combines the resulting sequences into one sequence.
794
823
/// </summary>
@@ -991,9 +1020,9 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull]
991
1020
992
1021
return source . Provider . CreateQuery ( optimized ) ;
993
1022
}
994
- #endregion SelectMany
1023
+ #endregion SelectMany
995
1024
996
- #region Single/SingleOrDefault
1025
+ #region Single/SingleOrDefault
997
1026
/// <summary>
998
1027
/// Returns the only element of a sequence, and throws an exception if there
999
1028
/// is not exactly one element in the sequence.
@@ -1030,9 +1059,9 @@ public static dynamic SingleOrDefault([NotNull] this IQueryable source)
1030
1059
var optimized = OptimizeExpression ( Expression . Call ( typeof ( Queryable ) , "SingleOrDefault" , new [ ] { source . ElementType } , source . Expression ) ) ;
1031
1060
return source . Provider . Execute ( optimized ) ;
1032
1061
}
1033
- #endregion Single/SingleOrDefault
1062
+ #endregion Single/SingleOrDefault
1034
1063
1035
- #region Skip
1064
+ #region Skip
1036
1065
private static readonly MethodInfo _skip = GetMethod ( nameof ( Queryable . Skip ) , 1 ) ;
1037
1066
1038
1067
/// <summary>
@@ -1052,9 +1081,9 @@ public static IQueryable Skip([NotNull] this IQueryable source, int count)
1052
1081
1053
1082
return CreateQuery ( _skip , source , Expression . Constant ( count ) ) ;
1054
1083
}
1055
- #endregion Skip
1084
+ #endregion Skip
1056
1085
1057
- #region SkipWhile
1086
+ #region SkipWhile
1058
1087
private static readonly MethodInfo _skipWhilePredicate = GetMethod ( nameof ( Queryable . SkipWhile ) , 1 , _predicateParameterHas2 ) ;
1059
1088
1060
1089
/// <summary>
@@ -1081,9 +1110,9 @@ public static IQueryable SkipWhile([NotNull] this IQueryable source, [NotNull] s
1081
1110
1082
1111
return CreateQuery ( _skipWhilePredicate , source , lambda ) ;
1083
1112
}
1084
- #endregion SkipWhile
1113
+ #endregion SkipWhile
1085
1114
1086
- #region Sum
1115
+ #region Sum
1087
1116
/// <summary>
1088
1117
/// Computes the sum of a sequence of numeric values.
1089
1118
/// </summary>
@@ -1096,9 +1125,9 @@ public static object Sum([NotNull] this IQueryable source)
1096
1125
var optimized = OptimizeExpression ( Expression . Call ( typeof ( Queryable ) , "Sum" , null , source . Expression ) ) ;
1097
1126
return source . Provider . Execute ( optimized ) ;
1098
1127
}
1099
- #endregion Sum
1128
+ #endregion Sum
1100
1129
1101
- #region Take
1130
+ #region Take
1102
1131
private static readonly MethodInfo _take = GetMethod ( nameof ( Queryable . Take ) , 1 ) ;
1103
1132
/// <summary>
1104
1133
/// Returns a specified number of contiguous elements from the start of a sequence.
@@ -1113,9 +1142,9 @@ public static IQueryable Take([NotNull] this IQueryable source, int count)
1113
1142
1114
1143
return CreateQuery ( _take , source , Expression . Constant ( count ) ) ;
1115
1144
}
1116
- #endregion Take
1145
+ #endregion Take
1117
1146
1118
- #region TakeWhile
1147
+ #region TakeWhile
1119
1148
private static readonly MethodInfo _takeWhilePredicate = GetMethod ( nameof ( Queryable . TakeWhile ) , 1 , _predicateParameterHas2 ) ;
1120
1149
1121
1150
/// <summary>
@@ -1144,7 +1173,7 @@ public static IQueryable TakeWhile([NotNull] this IQueryable source, [NotNull] s
1144
1173
}
1145
1174
#endregion TakeWhile
1146
1175
1147
- #region ThenBy
1176
+ #region ThenBy
1148
1177
/// <summary>
1149
1178
/// Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
1150
1179
/// </summary>
@@ -1205,9 +1234,9 @@ public static IOrderedQueryable ThenBy([NotNull] this IOrderedQueryable source,
1205
1234
var optimized = OptimizeExpression ( queryExpr ) ;
1206
1235
return ( IOrderedQueryable ) source . Provider . CreateQuery ( optimized ) ;
1207
1236
}
1208
- #endregion OrderBy
1237
+ #endregion OrderBy
1209
1238
1210
- #region Where
1239
+ #region Where
1211
1240
/// <summary>
1212
1241
/// Filters a sequence of values based on a predicate.
1213
1242
/// </summary>
@@ -1260,9 +1289,9 @@ public static IQueryable Where([NotNull] this IQueryable source, [NotNull] strin
1260
1289
var optimized = OptimizeExpression ( Expression . Call ( typeof ( Queryable ) , "Where" , new [ ] { source . ElementType } , source . Expression , Expression . Quote ( lambda ) ) ) ;
1261
1290
return source . Provider . CreateQuery ( optimized ) ;
1262
1291
}
1263
- #endregion
1292
+ #endregion
1264
1293
1265
- #region Private Helpers
1294
+ #region Private Helpers
1266
1295
// Code below is based on https://github.com/aspnet/EntityFramework/blob/9186d0b78a3176587eeb0f557c331f635760fe92/src/Microsoft.EntityFrameworkCore/EntityFrameworkQueryableExtensions.cs
1267
1296
1268
1297
private static IQueryable CreateQuery ( MethodInfo operatorMethodInfo , IQueryable source )
@@ -1341,6 +1370,6 @@ private static MethodInfo GetMethod<TResult>(string name, int parameterCount = 0
1341
1370
1342
1371
private static MethodInfo GetMethod ( string name , int parameterCount = 0 , Func < MethodInfo , bool > predicate = null ) =>
1343
1372
typeof ( Queryable ) . GetTypeInfo ( ) . GetDeclaredMethods ( name ) . Single ( mi => ( mi . GetParameters ( ) . Length == parameterCount + 1 ) && ( ( predicate == null ) || predicate ( mi ) ) ) ;
1344
- #endregion Private Helpers
1373
+ #endregion Private Helpers
1345
1374
}
1346
1375
}
0 commit comments