Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bf8e2ef

Browse files
committed
Fixes #1483: Regression with computed in $orderby with 'Could not find a property named xxx on xxx type"
Update the release version to 8.3.1
1 parent 40f07af commit bf8e2ef

File tree

9 files changed

+120
-18
lines changed

9 files changed

+120
-18
lines changed

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9125,10 +9125,10 @@
91259125
</summary>
91269126
<remarks>
91279127
Flattening properties referenced in an aggregate clause helps prevent the generation of nested queries by Entity Framework.
9128-
For example, given a query like:
9129-
groupby((A), aggregate(B/C with max as Alias1, B/D with max as Alias2))
9130-
The expression is rewritten as:
9131-
.Select($it => new FlatteningWrapper() {
9128+
For example, given a query like: <c>groupby((A), aggregate(B/C with max as Alias1, B/D with max as Alias2))</c>
9129+
the expression is rewritten as:
9130+
<code>
9131+
.Select($it => new FlatteningWrapper&lt;T&gt; {
91329132
Source = $it,
91339133
Container = new {
91349134
Value = $it.B.C,
@@ -9137,6 +9137,7 @@
91379137
}
91389138
}
91399139
})
9140+
</code>
91409141
A mapping is also maintained between the original properties and their flattened expressions:
91419142
B/C → $it.Container.Value
91429143
B/D → $it.Container.Next.Value
@@ -9462,8 +9463,7 @@
94629463
<param name="context">An instance of the <see cref="T:Microsoft.AspNetCore.OData.Query.Expressions.QueryBinderContext"/>.</param>
94639464
<remarks>
94649465
Generates an expression structured like:
9465-
$it => new ComputeWrapper&lt;T&gt;
9466-
{
9466+
$it => new ComputeWrapper&lt;T&gt; {
94679467
Instance = $it,
94689468
Model = parametrized(IEdmModel),
94699469
Container => new AggregationPropertyContainer() {
@@ -9472,6 +9472,7 @@
94729472
Next = new LastInChain() {
94739473
Name = "C",
94749474
Value = $it.A * $it.B
9475+
}
94759476
}
94769477
}
94779478
</remarks>

src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ private OrderByQueryOption GenerateDefaultOrderBy(ODataQueryContext context, Lis
697697
if (applySortOptions != null)
698698
{
699699
orderByRaw = String.Join(",", applySortOptions);
700-
return new OrderByQueryOption(orderByRaw, context, Apply.RawValue);
700+
return new OrderByQueryOption(orderByRaw, context, Apply.RawValue, Compute?.RawValue);
701701
}
702702
else
703703
{
@@ -749,7 +749,7 @@ private OrderByQueryOption EnsureStableSortOrderBy(OrderByQueryOption orderBy, O
749749
if (propertyPathsToAdd.Any())
750750
{
751751
var orderByRaw = orderBy.RawValue + "," + String.Join(",", propertyPathsToAdd);
752-
orderBy = new OrderByQueryOption(orderByRaw, context, Apply.RawValue);
752+
orderBy = new OrderByQueryOption(orderByRaw, context, Apply.RawValue, Compute?.RawValue);
753753
}
754754
}
755755
else
@@ -762,7 +762,7 @@ private OrderByQueryOption EnsureStableSortOrderBy(OrderByQueryOption orderBy, O
762762
// the sort stable but preserving the user's original intent for the major
763763
// sort order.
764764
var orderByRaw = orderBy.RawValue + "," + string.Join(",", propertiesToAdd.Select(p => p.Name));
765-
orderBy = new OrderByQueryOption(orderByRaw, context);
765+
orderBy = new OrderByQueryOption(orderByRaw, context, null, Compute?.RawValue);
766766
}
767767
}
768768

src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public OrderByQueryOption(string rawValue, ODataQueryContext context, ODataQuery
5656
_queryOptionParser = queryOptionParser;
5757
}
5858

59-
internal OrderByQueryOption(string rawValue, ODataQueryContext context, string applyRaw)
59+
internal OrderByQueryOption(string rawValue, ODataQueryContext context, string applyRaw, string computeRaw)
6060
{
6161
if (context == null)
6262
{
@@ -68,19 +68,27 @@ internal OrderByQueryOption(string rawValue, ODataQueryContext context, string a
6868
throw Error.ArgumentNullOrEmpty("rawValue");
6969
}
7070

71-
if (applyRaw == null)
72-
{
73-
throw Error.ArgumentNullOrEmpty("applyRaw");
74-
}
75-
7671
Context = context;
7772
RawValue = rawValue;
7873
Validator = context.GetOrderByQueryValidator();
74+
75+
Dictionary<string, string> queryOptions = new Dictionary<string, string>();
76+
queryOptions["$orderby"] = rawValue;
77+
if (applyRaw != null)
78+
{
79+
queryOptions["$apply"] = applyRaw;
80+
}
81+
82+
if (computeRaw != null)
83+
{
84+
queryOptions["$compute"] = computeRaw;
85+
}
86+
7987
_queryOptionParser = new ODataQueryOptionParser(
8088
context.Model,
8189
context.ElementType,
8290
context.NavigationSource,
83-
new Dictionary<string, string> { { "$orderby", rawValue }, { "$apply", applyRaw } },
91+
queryOptions,
8492
context.RequestContainer);
8593

8694
if (context.RequestContainer == null)
@@ -89,7 +97,15 @@ internal OrderByQueryOption(string rawValue, ODataQueryContext context, string a
8997
_queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver;
9098
}
9199

92-
_queryOptionParser.ParseApply();
100+
if (computeRaw != null)
101+
{
102+
_queryOptionParser.ParseCompute();
103+
}
104+
105+
if (applyRaw != null)
106+
{
107+
_queryOptionParser.ParseApply();
108+
}
93109
}
94110

95111
// This constructor is intended for unit testing only.

test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeController.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// </copyright>
66
//------------------------------------------------------------------------------
77

8+
using System;
89
using System.Linq;
910
using Microsoft.AspNetCore.Mvc;
1011
using Microsoft.AspNetCore.OData.Query;
@@ -50,5 +51,17 @@ public IActionResult GetSales()
5051
{
5152
return Ok();
5253
}
54+
55+
[HttpGet("odata/Shoppers")]
56+
public IQueryable Get(ODataQueryOptions<ComputeShopper> queryOptions)
57+
{
58+
var queryable = DollarComputeDataSource.Shoppers.AsQueryable();
59+
60+
return queryOptions.ApplyTo(queryable, new ODataQuerySettings
61+
{
62+
PageSize = 3,
63+
TimeZone = TimeZoneInfo.Utc
64+
});
65+
}
5366
}
5467
}

test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeDataModel.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,13 @@ public class ComputeSale
4949

5050
public IDictionary<string, object> Dynamics { get; set; } = new Dictionary<string, object>();
5151
}
52+
53+
public class ComputeShopper
54+
{
55+
public int Id { get; set; }
56+
57+
public string Name { get; set; }
58+
59+
public int Age { get; set; }
60+
}
5261
}

test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeDataSource.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ namespace Microsoft.AspNetCore.OData.E2E.Tests.DollarCompute
1313
public class DollarComputeDataSource
1414
{
1515
private static IList<ComputeCustomer> _customers;
16+
private static IList<ComputeShopper> _shoppers;
1617

1718
static DollarComputeDataSource()
1819
{
1920
GenerateCustomers();
2021
}
2122

2223
public static IList<ComputeCustomer> Customers => _customers;
24+
public static IList<ComputeShopper> Shoppers => _shoppers;
2325

2426
private static void GenerateCustomers()
2527
{
@@ -44,6 +46,12 @@ private static void GenerateCustomers()
4446
_customers[i - 1].Sales = Enumerable.Range(0, i + 3)
4547
.Select(idx => new ComputeSale { Id = 100 * i + idx, Amount = idx + i, Price = (3.1 + idx) * i, TaxRate = (0.1 + idx + i) / 10.0 }).ToList();
4648
}
49+
50+
_shoppers = new List<ComputeShopper>(_customers.Count);
51+
foreach (var c in _customers)
52+
{
53+
_shoppers.Add(new ComputeShopper { Id = c.Id, Name = c.Name, Age = c.Age });
54+
}
4755
}
4856
}
4957
}

test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeEdmModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public static IEdmModel GetEdmModel()
1717
var builder = new ODataConventionModelBuilder();
1818
builder.EntitySet<ComputeCustomer>("Customers");
1919
builder.EntitySet<ComputeSale>("Sales");
20+
21+
builder.EntitySet<ComputeShopper>("Shoppers");
2022
IEdmModel model = builder.GetEdmModel();
2123
return model;
2224
}

test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,5 +365,58 @@ public async Task QuerySales_ThrowsNotAllowed_IncludesDollarCompute_WithAllowedQ
365365
"Query option 'Compute' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings", payload);
366366
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
367367
}
368+
369+
[Fact]
370+
public async Task QueryShoppers_UsingQueryOptionsWithQuerySettings_IncludesDollarCompute_WorksWithDollarSelect()
371+
{
372+
// Arrange
373+
string queryUrl = "odata/shoppers?$compute=age add 10 as agePlusTen&$select=id,name,agePlusTen";
374+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
375+
HttpClient client = CreateClient();
376+
HttpResponseMessage response;
377+
378+
// Act
379+
response = await client.SendAsync(request);
380+
381+
// Assert
382+
string payload = await response.Content.ReadAsStringAsync();
383+
384+
Assert.NotNull(response);
385+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
386+
Assert.NotNull(response.Content);
387+
388+
// It returns 3 entities because there's a `pagesize=3` setting in the controller/action
389+
Assert.Equal("[{\"Id\":1,\"Name\":\"Peter\",\"agePlusTen\":29}," +
390+
"{\"Id\":2,\"Name\":\"Sam\",\"agePlusTen\":50}," +
391+
"{\"Id\":3,\"Name\":\"John\",\"agePlusTen\":44}]", payload);
392+
}
393+
394+
[Fact]
395+
public async Task QueryShoppers_UsingQueryOptionsWithQuerySettings_IncludesDollarCompute_WorksWithDollorOrderby()
396+
{
397+
// Arrange
398+
string queryUrl = "odata/shoppers?$compute=age add 10 as agePlusTen&$orderby=agePlusTen desc&$select=id,name,agePlusTen&$filter=agePlusTen ge 21";
399+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl);
400+
HttpClient client = CreateClient();
401+
HttpResponseMessage response;
402+
403+
// Act
404+
response = await client.SendAsync(request);
405+
406+
// Assert
407+
string payload = await response.Content.ReadAsStringAsync();
408+
409+
Assert.NotNull(response);
410+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
411+
Assert.NotNull(response.Content);
412+
413+
// It returns 3 entities because there's a `pagesize=3` setting in the controller/action
414+
// But the payload returns the ordered list.
415+
Assert.Equal("[" +
416+
"{\"Id\":2,\"Name\":\"Sam\",\"agePlusTen\":50}," +
417+
"{\"Id\":3,\"Name\":\"John\",\"agePlusTen\":44}," +
418+
"{\"Id\":4,\"Name\":\"Kerry\",\"agePlusTen\":39}]",
419+
payload);
420+
}
368421
}
369422
}

tool/builder.versions.settings.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<VersionMajor Condition="'$(VersionMajor)' == ''">8</VersionMajor>
55
<VersionMinor Condition="'$(VersionMinor)' == ''">3</VersionMinor>
6-
<VersionBuild Condition="'$(VersionBuild)' == ''">0</VersionBuild>
6+
<VersionBuild Condition="'$(VersionBuild)' == ''">1</VersionBuild>
77
<VersionRelease Condition="'$(VersionRelease)' == ''"></VersionRelease>
88
</PropertyGroup>
99

0 commit comments

Comments
 (0)