Skip to content

Commit 69cd5da

Browse files
committed
Restructure ComputeBinder for extensibility
1 parent eeb1d57 commit 69cd5da

26 files changed

+998
-296
lines changed

src/Microsoft.AspNetCore.OData/Abstracts/ContainerBuilderExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public static IContainerBuilder AddDefaultWebApiServices(this IContainerBuilder
111111
builder.AddService<IOrderByBinder, OrderByBinder>(ServiceLifetime.Singleton);
112112
builder.AddService<ISelectExpandBinder, SelectExpandBinder>(ServiceLifetime.Singleton);
113113
builder.AddService<IAggregationBinder, AggregationBinder>(ServiceLifetime.Singleton);
114+
builder.AddService<IComputeBinder, ComputeBinder>(ServiceLifetime.Singleton);
114115

115116
// HttpRequestScope.
116117
builder.AddService<HttpRequestScope>(ServiceLifetime.Scoped);

src/Microsoft.AspNetCore.OData/Common/TypeHelper.cs

+52-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,58 @@ public static bool IsDynamicTypeWrapper(this Type type)
4141

4242
public static bool IsSelectExpandWrapper(this Type type, out Type entityType) => IsTypeWrapper(typeof(SelectExpandWrapper<>), type, out entityType);
4343

44-
public static bool IsComputeWrapper(this Type type, out Type entityType) => IsTypeWrapper(typeof(ComputeWrapper<>), type, out entityType);
44+
/// <summary>
45+
/// Determines whether the specified type is a <see cref="ComputeWrapper{T}"/> or a custom implementation
46+
/// that inherits from <see cref="DynamicTypeWrapper"/> and implements both <see cref="IComputeWrapper{T}"/>
47+
/// and <see cref="IGroupByWrapper{TContainer, TWrapper}"/>.
48+
/// </summary>
49+
/// <param name="typeToCheck">The type to check.</param>
50+
/// <param name="entityType">The entity type if the specified type is a <see cref="ComputeWrapper{T}"/> or a custom implementation; otherwise, null.</param>
51+
/// <returns>
52+
/// <c>true</c> if the specified type is a <see cref="ComputeWrapper{T}"/> or a custom implementation
53+
/// that meets the criteria; otherwise, <c>false</c>.
54+
/// </returns>
55+
public static bool IsComputeWrapper(this Type typeToCheck, out Type entityType)
56+
{
57+
entityType = null;
58+
if (typeToCheck == null)
59+
{
60+
return false;
61+
}
62+
63+
if (typeToCheck.IsGenericType)
64+
{
65+
Type genericTypeDefinition = typeToCheck.GetGenericTypeDefinition();
66+
67+
// Default implementation
68+
if (genericTypeDefinition == typeof(ComputeWrapper<>))
69+
{
70+
entityType = typeToCheck.GetGenericArguments()[0];
71+
72+
Debug.Assert(
73+
typeof(DynamicTypeWrapper).IsAssignableFrom(genericTypeDefinition)
74+
&& genericTypeDefinition.ImplementsInterface(typeof(IComputeWrapper<>))
75+
&& genericTypeDefinition.ImplementsInterface(typeof(IGroupByWrapper<,>)),
76+
"ComputeWrapper<T> must inherit from DynamicTypeWrapper and implement IComputeWrapper<T> and IGroupByWrapper<TContainer, TWrapper>");
77+
78+
return true;
79+
}
80+
81+
// Custom implementation
82+
// Must inherit from DynamicTypeWrapper
83+
// Must implement IComputeWrapper<T> and IGroupByWrapper<TContainer, TWrapper>
84+
if (typeof(DynamicTypeWrapper).IsAssignableFrom(genericTypeDefinition)
85+
&& genericTypeDefinition.ImplementsInterface(typeof(IComputeWrapper<>))
86+
&& genericTypeDefinition.ImplementsInterface(typeof(IGroupByWrapper<,>)))
87+
{
88+
entityType = typeToCheck.GetGenericArguments()[0];
89+
90+
return true;
91+
}
92+
}
93+
94+
return false;
95+
}
4596

4697
/// <summary>
4798
/// Determines whether the specified type is a <see cref="FlatteningWrapper{T}"/> or a custom implementation

src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml

+119-13
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,19 @@
10881088
<param name="type">The test type.</param>
10891089
<returns>true/false</returns>
10901090
</member>
1091+
<member name="M:Microsoft.AspNetCore.OData.Common.TypeHelper.IsComputeWrapper(System.Type,System.Type@)">
1092+
<summary>
1093+
Determines whether the specified type is a <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.ComputeWrapper`1"/> or a custom implementation
1094+
that inherits from <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.DynamicTypeWrapper"/> and implements both <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1"/>
1095+
and <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.IGroupByWrapper`2"/>.
1096+
</summary>
1097+
<param name="typeToCheck">The type to check.</param>
1098+
<param name="entityType">The entity type if the specified type is a <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.ComputeWrapper`1"/> or a custom implementation; otherwise, null.</param>
1099+
<returns>
1100+
<c>true</c> if the specified type is a <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.ComputeWrapper`1"/> or a custom implementation
1101+
that meets the criteria; otherwise, <c>false</c>.
1102+
</returns>
1103+
</member>
10911104
<member name="M:Microsoft.AspNetCore.OData.Common.TypeHelper.IsFlatteningWrapper(System.Type)">
10921105
<summary>
10931106
Determines whether the specified type is a <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.FlatteningWrapper`1"/> or a custom implementation
@@ -9248,6 +9261,26 @@
92489261
<param name="resultClrType">The type of wrapper used to create an expression from the $apply parse tree.</param>
92499262
<returns>The applied result.</returns>
92509263
</member>
9264+
<member name="M:Microsoft.AspNetCore.OData.Query.Expressions.BinderExtensions.ApplyBind(Microsoft.AspNetCore.OData.Query.Expressions.IComputeBinder,System.Linq.IQueryable,Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode,Microsoft.AspNetCore.OData.Query.Expressions.QueryBinderContext,System.Type@)">
9265+
<summary>
9266+
Translate an OData parse tree represented by <see cref="T:Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode"/> to
9267+
an <see cref="T:System.Linq.Expressions.Expression"/> and applies it to an <see cref="T:System.Linq.IQueryable"/>.
9268+
</summary>
9269+
<param name="binder">An instance of the <see cref="T:Microsoft.AspNetCore.OData.Query.Expressions.IComputeBinder"/>.</param>
9270+
<param name="source">The original <see cref="T:System.Linq.IQueryable"/> source.</param>
9271+
<param name="computeTransformationNode">The <see cref="T:Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode"/> representing an OData parse tree.</param>
9272+
<param name="context">An instance of the <see cref="T:Microsoft.AspNetCore.OData.Query.Expressions.QueryBinderContext"/> containing the current query context.</param>
9273+
<param name="resultClrType">The type of wrapper used to create an expression from the $apply parse tree.</param>
9274+
<returns>The modified <see cref="T:System.Linq.IQueryable"/> source.</returns>
9275+
</member>
9276+
<member name="T:Microsoft.AspNetCore.OData.Query.Expressions.ComputeBinder">
9277+
<summary>
9278+
The default implementation to bind an OData $apply parse tree represented by a <see cref="T:Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode"/> to an <see cref="T:System.Linq.Expressions.Expression"/>.
9279+
</summary>
9280+
</member>
9281+
<member name="M:Microsoft.AspNetCore.OData.Query.Expressions.ComputeBinder.BindCompute(Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode,Microsoft.AspNetCore.OData.Query.Expressions.QueryBinderContext)">
9282+
<inheritdoc/>
9283+
</member>
92519284
<member name="T:Microsoft.AspNetCore.OData.Query.Expressions.ExpressionBinderBase">
92529285
<summary>
92539286
The base class for all expression binders.
@@ -9451,6 +9484,35 @@
94519484
</code>
94529485
</remarks>
94539486
</member>
9487+
<member name="T:Microsoft.AspNetCore.OData.Query.Expressions.IComputeBinder">
9488+
<summary>
9489+
Exposes the ability to translate an OData $apply parse tree represented by a <see cref="T:Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode"/> to an <see cref="T:System.Linq.Expressions.Expression"/>.
9490+
</summary>
9491+
</member>
9492+
<member name="M:Microsoft.AspNetCore.OData.Query.Expressions.IComputeBinder.BindCompute(Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode,Microsoft.AspNetCore.OData.Query.Expressions.QueryBinderContext)">
9493+
<summary>
9494+
Translates an OData $apply parse tree represented by a <see cref="T:Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode"/> to
9495+
an <see cref="T:System.Linq.Expressions.Expression"/>.
9496+
</summary>
9497+
<param name="computeTransformationNode">The OData $apply parse tree represented by <see cref="T:Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode"/>.</param>
9498+
<param name="context">An instance of the <see cref="T:Microsoft.AspNetCore.OData.Query.Expressions.QueryBinderContext"/>.</param>
9499+
<remarks>
9500+
Generates an expression structured like:
9501+
$it => new ComputeWrapper&lt;T&gt;
9502+
{
9503+
Instance = $it,
9504+
Model = parametrized(IEdmModel),
9505+
Container => new AggregationPropertyContainer() {
9506+
Name = "Z",
9507+
Value = $it.X + $it.Y,
9508+
Next = new LastInChain() {
9509+
Name = "C",
9510+
Value = $it.A * $it.B
9511+
}
9512+
}
9513+
</remarks>
9514+
<returns>The generated LINQ expression representing the OData $apply parse tree.</returns>
9515+
</member>
94549516
<member name="T:Microsoft.AspNetCore.OData.Query.Expressions.IFilterBinder">
94559517
<summary>
94569518
Exposes the ability to translate an OData $filter represented by <see cref="T:Microsoft.OData.UriParser.FilterClause"/> to the <see cref="T:System.Linq.Expressions.Expression"/>.
@@ -10495,18 +10557,6 @@
1049510557
<param name="model">The <see cref="T:Microsoft.OData.Edm.IEdmModel"/>.</param>
1049610558
<returns>The <see cref="T:System.Linq.Expressions.Expression"/> with derived types if any are present.</returns>
1049710559
</member>
10498-
<member name="P:Microsoft.AspNetCore.OData.Query.Expressions.TransformationBinderBase.ResultClrType">
10499-
<summary>
10500-
Gets CLR type returned from the query.
10501-
</summary>
10502-
</member>
10503-
<member name="M:Microsoft.AspNetCore.OData.Query.Expressions.TransformationBinderBase.IsClassicEF(System.Linq.IQueryable)">
10504-
<summary>
10505-
Checks IQueryable provider for need of EF6 optimization
10506-
</summary>
10507-
<param name="query"></param>
10508-
<returns>True if EF6 optimization are needed.</returns>
10509-
</member>
1051010560
<member name="T:Microsoft.AspNetCore.OData.Query.Expressions.UriFunctionsBinder">
1051110561
<summary>
1051210562
This class helps to bind uri functions to CLR.
@@ -10893,6 +10943,13 @@
1089310943
<param name="context">The query context.</param>
1089410944
<returns>The built <see cref="T:Microsoft.AspNetCore.OData.Query.Expressions.IAggregationBinder"/>.</returns>
1089510945
</member>
10946+
<member name="M:Microsoft.AspNetCore.OData.Query.ODataQueryContextExtensions.GetComputeBinder(Microsoft.AspNetCore.OData.Query.ODataQueryContext)">
10947+
<summary>
10948+
Gets the <see cref="T:Microsoft.AspNetCore.OData.Query.Expressions.IComputeBinder"/>.
10949+
</summary>
10950+
<param name="context">The query context.</param>
10951+
<returns>The built <see cref="T:Microsoft.AspNetCore.OData.Query.Expressions.IComputeBinder"/>.</returns>
10952+
</member>
1089610953
<member name="M:Microsoft.AspNetCore.OData.Query.ODataQueryContextExtensions.GetAssemblyResolver(Microsoft.AspNetCore.OData.Query.ODataQueryContext)">
1089710954
<summary>
1089810955
Gets the <see cref="T:Microsoft.OData.ModelBuilder.IAssemblyResolver"/>.
@@ -11595,6 +11652,12 @@
1159511652
<member name="F:Microsoft.AspNetCore.OData.Query.QueryConstants.FlatteningWrapperSourceProperty">
1159611653
<summary>Name for <see cref="P:Microsoft.AspNetCore.OData.Query.Wrapper.IFlatteningWrapper`1.Source"/> property.</summary>
1159711654
</member>
11655+
<member name="F:Microsoft.AspNetCore.OData.Query.QueryConstants.ComputeWrapperInstanceProperty">
11656+
<summary>Name for <see cref="P:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1.Instance"/> property.</summary>
11657+
</member>
11658+
<member name="F:Microsoft.AspNetCore.OData.Query.QueryConstants.ComputeWrapperModelProperty">
11659+
<summary>Name for <see cref="P:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1.Model"/> property.</summary>
11660+
</member>
1159811661
<member name="T:Microsoft.AspNetCore.OData.Query.QueryFilterProvider">
1159911662
<summary>
1160011663
An implementation of <see cref="T:Microsoft.AspNetCore.Mvc.Filters.IFilterProvider" /> that applies an action filter to
@@ -13259,6 +13322,15 @@
1325913322
<exception cref="T:System.InvalidOperationException">Thrown if <paramref name="flattenedExpressionType"/>
1326013323
does not implement the required interfaces or inherit from the required base class.</exception>
1326113324
</member>
13325+
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.QueryBinderValidator.ValidateComputeExpressionType(System.Type)">
13326+
<summary>
13327+
Validates that the provided type implements <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.IGroupByWrapper`2"/> and <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1"/>, and inherits from <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.DynamicTypeWrapper"/>.
13328+
</summary>
13329+
<param name="computeExpressionType">The type representing the flattened expression returned by
13330+
<see cref="M:Microsoft.AspNetCore.OData.Query.Expressions.IComputeBinder.BindCompute(Microsoft.OData.UriParser.Aggregation.ComputeTransformationNode,Microsoft.AspNetCore.OData.Query.Expressions.QueryBinderContext)"/>.</param>
13331+
<exception cref="T:System.InvalidOperationException">Thrown if <paramref name="computeExpressionType"/>
13332+
does not implement the required interfaces or inherit from the required base class.</exception>
13333+
</member>
1326213334
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.QueryBinderValidator.ValidateTransformationExpressionType(System.Type)">
1326313335
<summary>
1326413336
Validates that the provided type implements <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.IGroupByWrapper`2"/> and inherits from <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.DynamicTypeWrapper"/>.
@@ -13501,9 +13573,17 @@
1350113573
<param name="topQueryOption">The $top query.</param>
1350213574
<param name="validationSettings">The validation settings.</param>
1350313575
</member>
13576+
<member name="T:Microsoft.AspNetCore.OData.Query.Wrapper.ComputeWrapper`1">
13577+
<inheritdoc/>
13578+
</member>
13579+
<member name="P:Microsoft.AspNetCore.OData.Query.Wrapper.ComputeWrapper`1.Instance">
13580+
<summary>
13581+
Gets or sets the source object that provides the values used in the compute expression.
13582+
</summary>
13583+
</member>
1350413584
<member name="P:Microsoft.AspNetCore.OData.Query.Wrapper.ComputeWrapper`1.Model">
1350513585
<summary>
13506-
The Edm Model associated with the wrapper.
13586+
Gets or sets the Edm model associated with the wrapper.
1350713587
</summary>
1350813588
</member>
1350913589
<member name="T:Microsoft.AspNetCore.OData.Query.Wrapper.DynamicTypeWrapper">
@@ -13560,6 +13640,32 @@
1356013640
<member name="M:Microsoft.AspNetCore.OData.Query.Wrapper.GroupByWrapper.GetHashCode">
1356113641
<inheritdoc />
1356213642
</member>
13643+
<member name="T:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1">
13644+
<summary>
13645+
Represents a wrapper for a source object with computed values in an OData query.
13646+
</summary>
13647+
<typeparam name="T">The type of the source object.</typeparam>
13648+
<remarks>
13649+
The source object type can either be the type of the wrapped entity or another instance of <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1"/>
13650+
when compute expressions are chained.
13651+
For example, in the OData query:
13652+
<code>
13653+
/Sales?$apply=compute(Amount mul Product/TaxRate as Tax)/compute(Amount add Tax as SalesPrice)
13654+
</code>
13655+
In the first compute expression, the source object will be the wrapped "Sale" entity.
13656+
In the second compute expression, the source object will be an instance of <see cref="T:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1"/> where T is "Sale".
13657+
</remarks>
13658+
</member>
13659+
<member name="P:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1.Instance">
13660+
<summary>
13661+
Gets or sets the source object that provides the values used in the compute expression.
13662+
</summary>
13663+
</member>
13664+
<member name="P:Microsoft.AspNetCore.OData.Query.Wrapper.IComputeWrapper`1.Model">
13665+
<summary>
13666+
Gets or sets the Edm model associated with the wrapper.
13667+
</summary>
13668+
</member>
1356313669
<member name="T:Microsoft.AspNetCore.OData.Query.Wrapper.IFlatteningWrapper`1">
1356413670
<summary>
1356513671
Represents the result of flattening properties referenced in aggregate clause of a $apply query.

0 commit comments

Comments
 (0)