Skip to content

Commit 4fc6a89

Browse files
authored
Fixes #1483: Regression with computed in $orderby with 'Could not find a property named xxx on ....' (#1486)
* Fixes #1483: Regression with computed in $orderby with 'Could not find a property named xxx on ....' * Bump version to 9.3.2
1 parent 90d911e commit 4fc6a89

File tree

8 files changed

+114
-12
lines changed

8 files changed

+114
-12
lines changed

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,4 +51,16 @@ 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
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,12 @@ 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+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ 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
{
@@ -21,6 +22,8 @@ static DollarComputeDataSource()
2122

2223
public static IList<ComputeCustomer> Customers => _customers;
2324

25+
public static IList<ComputeShopper> Shoppers => _shoppers;
26+
2427
private static void GenerateCustomers()
2528
{
2629
_customers = new List<ComputeCustomer>
@@ -44,5 +47,11 @@ private static void GenerateCustomers()
4447
_customers[i - 1].Sales = Enumerable.Range(0, i + 3)
4548
.Select(idx => new ComputeSale { Id = 100 * i + idx, Amount = idx + i, Price = (3.1 + idx) * i, TaxRate = (0.1 + idx + i) / 10.0 }).ToList();
4649
}
50+
51+
_shoppers = new List<ComputeShopper>(_customers.Count);
52+
foreach (var c in _customers)
53+
{
54+
_shoppers.Add(new ComputeShopper { Id = c.Id, Name = c.Name, Age = c.Age });
55+
}
4756
}
4857
}

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,4 +365,57 @@ 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
}

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)' == ''">9</VersionMajor>
55
<VersionMinor Condition="'$(VersionMinor)' == ''">3</VersionMinor>
6-
<VersionBuild Condition="'$(VersionBuild)' == ''">1</VersionBuild>
6+
<VersionBuild Condition="'$(VersionBuild)' == ''">2</VersionBuild>
77
<VersionRelease Condition="'$(VersionRelease)' == ''"></VersionRelease>
88
</PropertyGroup>
99

0 commit comments

Comments
 (0)