3
3
4
4
using System ;
5
5
using System . Collections . Generic ;
6
+ using System . Diagnostics . CodeAnalysis ;
6
7
using System . Diagnostics . Contracts ;
7
8
using System . Globalization ;
8
9
using System . Linq ;
9
10
using System . Linq . Expressions ;
10
11
using System . Reflection ;
12
+ using Microsoft . AspNet . OData . Adapters ;
11
13
using Microsoft . AspNet . OData . Common ;
12
14
using Microsoft . AspNet . OData . Formatter ;
13
15
using Microsoft . AspNet . OData . Interfaces ;
16
+ using Microsoft . Extensions . DependencyInjection ;
14
17
using Microsoft . OData ;
15
18
using Microsoft . OData . Edm ;
16
19
using Microsoft . OData . UriParser ;
17
20
using Microsoft . OData . UriParser . Aggregation ;
18
21
19
22
namespace Microsoft . AspNet . OData . Query . Expressions
20
23
{
21
- internal class AggregationBinder : ExpressionBinderBase
24
+ /// <summary>
25
+ /// Translates an OData aggregate or groupby transformations of $apply parse tree represented by <see cref="TransformationNode"/> to
26
+ /// an <see cref="Expression"/> and applies it to an <see cref="IQueryable"/>.
27
+ /// </summary>
28
+ [ SuppressMessage ( "Microsoft.Maintainability" , "CA1506:AvoidExcessiveClassCoupling" , Justification = "Relies on many ODataLib classes." ) ]
29
+ public class AggregationBinder : ExpressionBinderBase
22
30
{
23
31
private const string GroupByContainerProperty = "GroupByContainer" ;
24
32
private Type _elementType ;
@@ -33,6 +41,25 @@ internal class AggregationBinder : ExpressionBinderBase
33
41
34
42
private bool _classicEF = false ;
35
43
44
+ /// <summary>
45
+ /// Initializes a new instance of the <see cref="AggregationBinder"/> class.
46
+ /// </summary>
47
+ /// <param name="settings">The <see cref="ODataQuerySettings"/> to use during binding.</param>
48
+ /// <param name="requestContainer">The request container.</param>
49
+ /// <param name="elementType">ClrType for result of transformations.</param>
50
+ /// <param name="model">The EDM model.</param>
51
+ /// <param name="transformation">The transformation node.</param>
52
+ protected internal AggregationBinder ( ODataQuerySettings settings , IServiceProvider requestContainer , Type elementType ,
53
+ IEdmModel model , TransformationNode transformation )
54
+ : this ( settings , requestContainer ? . GetService < IWebApiAssembliesResolver > ( ) ?? WebApiAssembliesResolver . Default ,
55
+ elementType , model , transformation )
56
+ {
57
+ // Notes for: ?? WebApiAssembliesResolver.Default
58
+ // The IWebApiAssembliesResolver service is internal and can only be injected by WebApi.
59
+ // This code path may be used in cases when the service container is not available
60
+ // and the service container is available but may not contain an instance of IWebApiAssembliesResolver.
61
+ }
62
+
36
63
internal AggregationBinder ( ODataQuerySettings settings , IWebApiAssembliesResolver assembliesResolver , Type elementType ,
37
64
IEdmModel model , TransformationNode transformation )
38
65
: base ( model , assembliesResolver , settings )
@@ -140,12 +167,16 @@ public Type ResultClrType
140
167
get ; private set ;
141
168
}
142
169
143
- public IEdmTypeReference ResultType
170
+ internal IEdmTypeReference ResultType
144
171
{
145
172
get ; private set ;
146
173
}
147
174
148
- public IQueryable Bind ( IQueryable query )
175
+ /// <summary>
176
+ /// Applies aggregate or groupby transformations of $apply query option to the given <see cref="IQueryable"/>.
177
+ /// </summary>
178
+ /// <param name="query">The original <see cref="IQueryable"/>.</param>
179
+ public virtual IQueryable Bind ( IQueryable query )
149
180
{
150
181
Contract . Assert ( query != null ) ;
151
182
@@ -353,8 +384,16 @@ private Expression CreateAggregationExpression(ParameterExpression accum, Aggreg
353
384
}
354
385
}
355
386
356
- private Expression CreateEntitySetAggregateExpression (
357
- ParameterExpression accum , EntitySetAggregateExpression expression , Type baseType )
387
+ /// <summary>
388
+ /// Binds a <see cref="EntitySetAggregateExpression"/> to create a LINQ <see cref="Expression"/> that
389
+ /// represents the semantics of the <see cref="EntitySetAggregateExpression"/>.
390
+ /// </summary>
391
+ /// <param name="accumulativeParameter"></param>
392
+ /// <param name="expression">The node to bind.</param>
393
+ /// <param name="baseType"></param>
394
+ /// <returns>The LINQ <see cref="Expression"/> created.</returns>
395
+ protected virtual Expression CreateEntitySetAggregateExpression (
396
+ ParameterExpression accumulativeParameter , EntitySetAggregateExpression expression , Type baseType )
358
397
{
359
398
// Should return following expression
360
399
// $it => $it.AsQueryable()
@@ -371,7 +410,7 @@ private Expression CreateEntitySetAggregateExpression(
371
410
372
411
List < MemberAssignment > wrapperTypeMemberAssignments = new List < MemberAssignment > ( ) ;
373
412
var asQueryableMethod = ExpressionHelperMethods . QueryableAsQueryable . MakeGenericMethod ( baseType ) ;
374
- Expression asQueryableExpression = Expression . Call ( null , asQueryableMethod , accum ) ;
413
+ Expression asQueryableExpression = Expression . Call ( null , asQueryableMethod , accumulativeParameter ) ;
375
414
376
415
// Create lambda to access the entity set from expression
377
416
var source = BindAccessor ( expression . Expression . Source ) ;
@@ -433,7 +472,15 @@ MethodInfo selectManyMethod
433
472
return Expression . Call ( null , selectMethod , groupedEntitySet , selectLambda ) ;
434
473
}
435
474
436
- private Expression CreatePropertyAggregateExpression ( ParameterExpression accum , AggregateExpression expression , Type baseType )
475
+ /// <summary>
476
+ /// Binds a <see cref="AggregateExpression"/> to create a LINQ <see cref="Expression"/> that
477
+ /// represents the semantics of the <see cref="AggregateExpression"/>.
478
+ /// </summary>
479
+ /// <param name="accumulativeParameter"></param>
480
+ /// <param name="expression">The node to bind.</param>
481
+ /// <param name="baseType"></param>
482
+ /// <returns>The LINQ <see cref="Expression"/> created.</returns>
483
+ protected virtual Expression CreatePropertyAggregateExpression ( ParameterExpression accumulativeParameter , AggregateExpression expression , Type baseType )
437
484
{
438
485
// accum type is IGrouping<,baseType> that implements IEnumerable<baseType>
439
486
// we need cast it to IEnumerable<baseType> during expression building (IEnumerable)$it
@@ -442,12 +489,12 @@ private Expression CreatePropertyAggregateExpression(ParameterExpression accum,
442
489
if ( _classicEF )
443
490
{
444
491
var asQuerableMethod = ExpressionHelperMethods . QueryableAsQueryable . MakeGenericMethod ( baseType ) ;
445
- asQuerableExpression = Expression . Call ( null , asQuerableMethod , accum ) ;
492
+ asQuerableExpression = Expression . Call ( null , asQuerableMethod , accumulativeParameter ) ;
446
493
}
447
494
else
448
495
{
449
496
var queryableType = typeof ( IEnumerable < > ) . MakeGenericType ( baseType ) ;
450
- asQuerableExpression = Expression . Convert ( accum , queryableType ) ;
497
+ asQuerableExpression = Expression . Convert ( accumulativeParameter , queryableType ) ;
451
498
}
452
499
453
500
// $count is a virtual property, so there's not a propertyLambda to create.
@@ -639,9 +686,27 @@ private Expression BindAccessor(QueryNode node, Expression baseElement = null)
639
686
}
640
687
}
641
688
642
- private Expression CreatePropertyAccessExpression ( Expression source , IEdmProperty property , string propertyPath = null )
689
+ /// <summary>
690
+ /// Returns an <see cref="Expression"/> that represents access to <paramref name="edmProperty"/>.
691
+ /// </summary>
692
+ /// <param name="edmProperty">The EDM property which access expression to return.</param>
693
+ /// <param name="source">The source that contains the <paramref name="edmProperty"/>.</param>
694
+ /// <returns>The property access <see cref="Expression"/>.</returns>
695
+ protected virtual Expression CreatePropertyAccessExpression ( Expression source , IEdmProperty edmProperty )
696
+ {
697
+ return CreatePropertyAccessExpression ( source , edmProperty , null ) ;
698
+ }
699
+
700
+ /// <summary>
701
+ /// Returns an <see cref="Expression"/> that represents access to <paramref name="edmProperty"/>.
702
+ /// </summary>
703
+ /// <param name="edmProperty">The EDM property which access expression to return.</param>
704
+ /// <param name="source">The source that contains the <paramref name="edmProperty"/>.</param>
705
+ /// <param name="propertyPath"></param>
706
+ /// <returns>The property access <see cref="Expression"/>.</returns>
707
+ protected virtual Expression CreatePropertyAccessExpression ( Expression source , IEdmProperty edmProperty , string propertyPath )
643
708
{
644
- string propertyName = EdmLibHelpers . GetClrPropertyName ( property , Model ) ;
709
+ string propertyName = EdmLibHelpers . GetClrPropertyName ( edmProperty , Model ) ;
645
710
propertyPath = propertyPath ?? propertyName ;
646
711
if ( QuerySettings . HandleNullPropagation == HandleNullPropagationOption . True && IsNullable ( source . Type ) &&
647
712
source != this . _lambdaParameter )
@@ -666,7 +731,13 @@ private Expression CreatePropertyAccessExpression(Expression source, IEdmPropert
666
731
}
667
732
}
668
733
669
- private Expression CreateOpenPropertyAccessExpression ( SingleValueOpenPropertyAccessNode openNode )
734
+ /// <summary>
735
+ /// Binds a <see cref="SingleValueOpenPropertyAccessNode"/> to create a LINQ <see cref="Expression"/> that
736
+ /// represents the semantics of the <see cref="SingleValueOpenPropertyAccessNode"/>.
737
+ /// </summary>
738
+ /// <param name="openNode">The node to bind.</param>
739
+ /// <returns>The LINQ <see cref="Expression"/> created.</returns>
740
+ protected virtual Expression CreateOpenPropertyAccessExpression ( SingleValueOpenPropertyAccessNode openNode )
670
741
{
671
742
Expression sourceAccessor = BindAccessor ( openNode . Source ) ;
672
743
0 commit comments