diff --git a/src/Microsoft.AspNetCore.OData/Query/ODataQueryContext.cs b/src/Microsoft.AspNetCore.OData/Query/ODataQueryContext.cs index 4ed011649..af7f2aede 100644 --- a/src/Microsoft.AspNetCore.OData/Query/ODataQueryContext.cs +++ b/src/Microsoft.AspNetCore.OData/Query/ODataQueryContext.cs @@ -26,6 +26,8 @@ namespace Microsoft.AspNetCore.OData.Query /// public class ODataQueryContext { + internal static readonly ODataUriResolver DefaultCaseInsensitiveResolver = new ODataUriResolver { EnableCaseInsensitive = true }; + private DefaultQueryConfigurations _defaultQueryConfigurations; /// diff --git a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs index 37a88ad07..293ce063d 100644 --- a/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs +++ b/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs @@ -843,6 +843,13 @@ internal void AddAutoSelectExpandProperties() Context.NavigationSource, queryParameters, Context.RequestContainer); // the Context.RequestContainer could be null for non-edm model + + if (Context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; + } + var originalSelectExpand = SelectExpand; SelectExpand = new SelectExpandQueryOption( autoSelectRawValue, @@ -1160,7 +1167,7 @@ private void Initialize(ODataQueryContext context) else { // By default, let's enable the property name case-insensitive - _queryOptionParser.Resolver = new ODataUriResolver { EnableCaseInsensitive = true }; + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; } BuildQueryOptions(normalizedQueryParameters); diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs index ac8b0e043..64608ea8c 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs @@ -77,6 +77,12 @@ internal ComputeQueryOption(string rawValue, ODataQueryContext context) context.NavigationSource, new Dictionary { { "$compute", rawValue } }, context.RequestContainer); + + if (context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; + } } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs index 7029b49c3..5e13d0253 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/CountQueryOption.cs @@ -73,6 +73,12 @@ internal CountQueryOption(string rawValue, ODataQueryContext context) context.NavigationSource, new Dictionary { { "$count", rawValue } }, context.RequestContainer); + + if (context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; + } } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs index 109524f6b..23319447b 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/FilterQueryOption.cs @@ -83,6 +83,12 @@ internal FilterQueryOption(string rawValue, ODataQueryContext context) context.NavigationSource, new Dictionary { { "$filter", rawValue } }, context.RequestContainer); + + if (context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; + } } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs index 0468b14b9..f680e7e33 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/OrderByQueryOption.cs @@ -82,6 +82,13 @@ internal OrderByQueryOption(string rawValue, ODataQueryContext context, string a context.NavigationSource, new Dictionary { { "$orderby", rawValue }, { "$apply", applyRaw } }, context.RequestContainer); + + if (context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; + } + _queryOptionParser.ParseApply(); } @@ -107,6 +114,12 @@ internal OrderByQueryOption(string rawValue, ODataQueryContext context) context.NavigationSource, new Dictionary { { "$orderby", rawValue } }, context.RequestContainer); + + if (context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver.EnableCaseInsensitive = true; + } } internal OrderByQueryOption(OrderByQueryOption orderBy) diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs index ac4d2563d..c2a5bf125 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs @@ -76,6 +76,12 @@ internal SearchQueryOption(string rawValue, ODataQueryContext context) context.NavigationSource, new Dictionary { { "$search", rawValue } }, context.RequestContainer); + + if (context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; + } } /// diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs index fa18aa719..f771c9ab6 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/SelectExpandQueryOption.cs @@ -94,6 +94,11 @@ internal SelectExpandQueryOption(string select, string expand, ODataQueryContext context.RequestContainer) : null) { + if (_queryOptionParser != null && context.RequestContainer == null) + { + // By default, let's enable the property name case-insensitive + _queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver; + } } /// diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/ODataOrderByTest/OrderByMoreTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/ODataOrderByTest/OrderByMoreTest.cs new file mode 100644 index 000000000..244ec36bb --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/ODataOrderByTest/OrderByMoreTest.cs @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.E2E.Tests.Extensions; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.TestCommon; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.ODataOrderByTest +{ + public class ODataOrderByMoreTest : WebApiTestBase + { + public ODataOrderByMoreTest(WebApiTestFixture fixture) + :base(fixture) + { + } + + protected static void UpdateConfigureServices(IServiceCollection services) + { + services.ConfigureControllers(typeof(BooksController)); + services.AddControllers() + .AddOData(); // without any configuration + } + + [Theory] + [InlineData("/books?orderby=ISBN&top=1")] + [InlineData("/books?orderby=isbn&top=1")] + [InlineData("/books?$orderby=ISBN&top=1")] + [InlineData("/books?$orderby=isbn&top=1")] + public async Task TestOrderBy_WithDifferentPropertyCase(string requestUri) + { + // Arrange + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri); + HttpClient client = CreateClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var resultArray = await response.Content.ReadAsObject(); + + JObject objectItem = Assert.IsType(Assert.Single(resultArray)); + Assert.Equal(2, objectItem.Properties().Count()); + Assert.Equal("063-6-920-02371-5", objectItem["isbn"]); + Assert.Equal(2, objectItem["id"]); + } + } + + [ApiController] + [Route("[controller]")] + public class BooksController : ControllerBase + { + private static IList _books = new List + { + new Book + { + Id = 1, + ISBN = "978-0-321-87758-1" + }, + new Book + { + Id = 2, + ISBN = "063-6-920-02371-5", + } + }; + + [HttpGet] + public IEnumerable Get(ODataQueryOptions queryOptions) + { + var queryable = (IQueryable)queryOptions.ApplyTo(_books.AsQueryable()); + return queryable.ToList(); + } + } + + public class Book + { + public int Id { get; set; } + public string ISBN { get; set; } + } +}