From f7a60480672f918e00d5b0d4f74d263ac84cc00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Mitermite?= Date: Wed, 12 Jan 2022 16:23:16 +0100 Subject: [PATCH 1/7] Adds dependency injection support in non-model scenario --- .../Extensions/HttpRequestExtensions.cs | 22 ++++++------ .../Microsoft.AspNetCore.OData.xml | 11 ++++-- .../ODataOptions.cs | 34 +++++++++++++------ .../PublicAPI.Unshipped.txt | 1 + 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs index 83beb1bc1..1cfad5fc8 100644 --- a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs @@ -271,12 +271,6 @@ public static IServiceProvider GetRouteServices(this HttpRequest request) return requestContainer; } - // if the prefixName == null, it's a non-model scenario - if (request.ODataFeature().RoutePrefix == null) - { - return null; - } - // HTTP routes will not have chance to call CreateRequestContainer. We have to call it. return request.CreateRouteServices(request.ODataFeature().RoutePrefix); } @@ -300,6 +294,11 @@ public static IServiceProvider CreateRouteServices(this HttpRequest request, str } IServiceScope requestScope = request.CreateRequestScope(routePrefix); + if (requestScope == null) + { + // non-model scenario with dependency injection non enabled + return null; + } IServiceProvider requestContainer = requestScope.ServiceProvider; request.ODataFeature().RequestScope = requestScope; @@ -342,13 +341,14 @@ private static IServiceScope CreateRequestScope(this HttpRequest request, string ODataOptions options = request.ODataOptions(); IServiceProvider rootContainer = options.GetRouteServices(routePrefix); - IServiceScope scope = rootContainer.GetRequiredService().CreateScope(); - - // Bind scoping request into the OData container. - if (!string.IsNullOrEmpty(routePrefix)) + if (rootContainer == null) { - scope.ServiceProvider.GetRequiredService().HttpRequest = request; + return null; } + IServiceScope scope = rootContainer.GetRequiredService().CreateScope(); + + // Bind scoping request into the OData container. + scope.ServiceProvider.GetRequiredService().HttpRequest = request; return scope; } diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 54a943a58..9af4ceaab 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -6102,6 +6102,13 @@ Gets the instance responsible for configuring the route template. + + + Enables dependency injection support for non-model scenario + + The configuring action to add the services to the container. + The current instance to enable fluent configuration. + Contains the OData instances and dependency injection containers for specific routes. @@ -6218,12 +6225,12 @@ Gets the query setting. - + Build the container. - The Edm model. The setup config. + The Edm model. The built service provider. diff --git a/src/Microsoft.AspNetCore.OData/ODataOptions.cs b/src/Microsoft.AspNetCore.OData/ODataOptions.cs index c15c3c1a1..29ddc1f43 100644 --- a/src/Microsoft.AspNetCore.OData/ODataOptions.cs +++ b/src/Microsoft.AspNetCore.OData/ODataOptions.cs @@ -62,6 +62,19 @@ public class ODataOptions /// public ODataRouteOptions RouteOptions { get; } = new ODataRouteOptions(); + private IServiceProvider ServiceProvider { get; set; } + + /// + /// Enables dependency injection support for non-model scenario + /// + /// The configuring action to add the services to the container. + /// The current instance to enable fluent configuration. + public ODataOptions EnableDependencyInjection(Action configureServices) + { + ServiceProvider = BuildContainer(configureServices); + return this; + } + #endregion #region RouteComponents @@ -144,7 +157,7 @@ public ODataOptions AddRouteComponents(string routePrefix, IEdmModel model, Acti // Consider to use Lazy ? - IServiceProvider serviceProvider = BuildRouteContainer(model, configureServices); + IServiceProvider serviceProvider = BuildContainer(configureServices, model); RouteComponents[sanitizedRoutePrefix] = (model, serviceProvider); return this; } @@ -158,7 +171,7 @@ public IServiceProvider GetRouteServices(string routePrefix) { if (routePrefix == null) { - return null; + return ServiceProvider; } string sanitizedRoutePrefix = SanitizeRoutePrefix(routePrefix); @@ -284,13 +297,11 @@ public ODataOptions SetMaxTop(int? maxTopValue) /// /// Build the container. /// - /// The Edm model. /// The setup config. + /// The Edm model. /// The built service provider. - private IServiceProvider BuildRouteContainer(IEdmModel model, Action setupAction) + private IServiceProvider BuildContainer(Action setupAction, IEdmModel model = null) { - Contract.Assert(model != null); - ServiceCollection services = new ServiceCollection(); DefaultContainerBuilder builder = new DefaultContainerBuilder(); @@ -306,10 +317,13 @@ private IServiceProvider BuildRouteContainer(IEdmModel model, Action(sp => new UnqualifiedODataUriResolver { EnableCaseInsensitive = true }); - // Inject the Edm model. - // From Current ODL implement, such injection only be used in reader and writer if the input - // model is null. - builder.Services.AddSingleton(sp => model); + if (model != null) + { + // Inject the Edm model. + // From Current ODL implement, such injection only be used in reader and writer if the input + // model is null. + builder.Services.AddSingleton(sp => model); + } // Inject the customized services. setupAction?.Invoke(builder.Services); diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 7ee211e0b..77d08b32c 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -657,6 +657,7 @@ Microsoft.AspNetCore.OData.ODataOptions.EnableAttributeRouting.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableAttributeRouting.set -> void Microsoft.AspNetCore.OData.ODataOptions.EnableContinueOnErrorHeader.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableContinueOnErrorHeader.set -> void +Microsoft.AspNetCore.OData.ODataOptions.EnableDependencyInjection(System.Action configureServices) -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.EnableNoDollarQueryOptions.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableNoDollarQueryOptions.set -> void Microsoft.AspNetCore.OData.ODataOptions.EnableQueryFeatures(int? maxTopValue = null) -> Microsoft.AspNetCore.OData.ODataOptions From c8013bd478f885f29879871dd4877fea6dc0ae9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Mitermite?= Date: Thu, 13 Jan 2022 11:03:48 +0100 Subject: [PATCH 2/7] Fixes failed tests --- .../Extensions/HttpRequestExtensions.cs | 8 ++++---- .../Microsoft.AspNetCore.OData.PublicApi.Net5.bsl | 1 + .../Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs index 1cfad5fc8..5bd3fb7ac 100644 --- a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs @@ -5,9 +5,6 @@ // //------------------------------------------------------------------------------ -using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.OData.Abstracts; using Microsoft.AspNetCore.OData.Common; @@ -18,6 +15,9 @@ using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; +using System; +using System.Collections.Generic; +using System.Linq; namespace Microsoft.AspNetCore.OData.Extensions { @@ -340,7 +340,7 @@ private static IServiceScope CreateRequestScope(this HttpRequest request, string { ODataOptions options = request.ODataOptions(); - IServiceProvider rootContainer = options.GetRouteServices(routePrefix); + IServiceProvider rootContainer = options?.GetRouteServices(routePrefix); if (rootContainer == null) { return null; diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl index 51c440846..ef794e6d0 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl @@ -118,6 +118,7 @@ public class Microsoft.AspNetCore.OData.ODataOptions { public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, Microsoft.AspNetCore.OData.Batch.ODataBatchHandler batchHandler) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions Count () + public Microsoft.AspNetCore.OData.ODataOptions EnableDependencyInjection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions EnableQueryFeatures (params System.Nullable`1[[System.Int32]] maxTopValue) public Microsoft.AspNetCore.OData.ODataOptions Expand () public Microsoft.AspNetCore.OData.ODataOptions Filter () diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl index 51c440846..ef794e6d0 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl @@ -118,6 +118,7 @@ public class Microsoft.AspNetCore.OData.ODataOptions { public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, Microsoft.AspNetCore.OData.Batch.ODataBatchHandler batchHandler) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions Count () + public Microsoft.AspNetCore.OData.ODataOptions EnableDependencyInjection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions EnableQueryFeatures (params System.Nullable`1[[System.Int32]] maxTopValue) public Microsoft.AspNetCore.OData.ODataOptions Expand () public Microsoft.AspNetCore.OData.ODataOptions Filter () From f92320a861c6844b4f0b850a8d43b00e7c69b0fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Mitermite?= Date: Mon, 4 Apr 2022 11:30:11 +0200 Subject: [PATCH 3/7] Renames EnableDependencyInjection method to ConfigureServiceCollection Adds a test case --- .../Microsoft.AspNetCore.OData.xml | 4 +- .../ODataOptions.cs | 4 +- .../PublicAPI.Unshipped.txt | 2 +- ...icrosoft.AspNetCore.OData.E2E.Tests.csproj | 2 +- .../NonEdm/ConfigureServiceCollectionTest.cs | 42 ++++++++++++++++++ .../NonEdm/NonEdmController.cs | 43 +++++++++++++++++++ .../NonEdm/NonEdmDataModel.cs | 15 +++++++ ...rosoft.AspNetCore.OData.PublicApi.Net5.bsl | 2 +- ...t.AspNetCore.OData.PublicApi.NetCore31.bsl | 2 +- 9 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmController.cs create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 70504c0af..9f8035336 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -6117,9 +6117,9 @@ Gets the instance responsible for configuring the route template. - + - Enables dependency injection support for non-model scenario + Configures service collection for non-EDM scenario The configuring action to add the services to the container. The current instance to enable fluent configuration. diff --git a/src/Microsoft.AspNetCore.OData/ODataOptions.cs b/src/Microsoft.AspNetCore.OData/ODataOptions.cs index 5106fc25e..0f0865d0d 100644 --- a/src/Microsoft.AspNetCore.OData/ODataOptions.cs +++ b/src/Microsoft.AspNetCore.OData/ODataOptions.cs @@ -65,11 +65,11 @@ public class ODataOptions private IServiceProvider ServiceProvider { get; set; } /// - /// Enables dependency injection support for non-model scenario + /// Configures service collection for non-EDM scenario /// /// The configuring action to add the services to the container. /// The current instance to enable fluent configuration. - public ODataOptions EnableDependencyInjection(Action configureServices) + public ODataOptions ConfigureServiceCollection(Action configureServices) { ServiceProvider = BuildContainer(configureServices); return this; diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 2580e255d..30e137256 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -659,7 +659,7 @@ Microsoft.AspNetCore.OData.ODataOptions.EnableAttributeRouting.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableAttributeRouting.set -> void Microsoft.AspNetCore.OData.ODataOptions.EnableContinueOnErrorHeader.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableContinueOnErrorHeader.set -> void -Microsoft.AspNetCore.OData.ODataOptions.EnableDependencyInjection(System.Action configureServices) -> Microsoft.AspNetCore.OData.ODataOptions +Microsoft.AspNetCore.OData.ODataOptions.ConfigureServiceCollection(System.Action configureServices) -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.EnableNoDollarQueryOptions.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableNoDollarQueryOptions.set -> void Microsoft.AspNetCore.OData.ODataOptions.EnableQueryFeatures(int? maxTopValue = null) -> Microsoft.AspNetCore.OData.ODataOptions diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj index d8ee916e2..49f9d8cf0 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj @@ -2,7 +2,7 @@ netcoreapp3.1;net5.0 - $(TargetFrameworks);net6.0 + $(TargetFrameworks);net6.0 Microsoft.AspNetCore.OData.E2E.Tests Microsoft.AspNetCore.OData.E2E.Tests diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs new file mode 100644 index 000000000..492e7a102 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.OData.E2E.Tests.Extensions; +using Microsoft.AspNetCore.OData.TestCommon; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OData.UriParser; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm +{ + public class ConfigureServiceCollectionTest : WebApiTestBase + { + public ConfigureServiceCollectionTest(WebApiTestFixture fixture) + : base(fixture) + { + } + + protected static void UpdateConfigureServices(IServiceCollection services) + { + services.ConfigureControllers(typeof(CustomersController)); + services.AddControllers().AddOData(opt => + { + opt.EnableQueryFeatures(); + opt.ConfigureServiceCollection(services => + { + services.AddSingleton(sp => new StringAsEnumResolver() { EnableCaseInsensitive = true }); + }); + }); + } + + [Fact] + public async Task EnableConfigureServiceCollectionTest() + { + HttpResponseMessage response = await CreateClient().SendAsync(new HttpRequestMessage(HttpMethod.Get, $"api/Customers?$filter=Gender eq 'MaLe'")); + var values = await response.Content.ReadAsObject(); + Assert.Equal(3, values.Count); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmController.cs new file mode 100644 index 000000000..718254a11 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmController.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Query; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm +{ + [ApiController] + [Route("api/[controller]")] + public class CustomersController : ControllerBase + { + [HttpGet] + public IActionResult Get(ODataQueryOptions options) + { + return Ok(options.ApplyTo(NonEdmDbContext.GetCustomers().AsQueryable())); + } + } + + public class NonEdmDbContext + { + private static IList _customers; + + public static IList GetCustomers() + { + if (_customers == null) + { + Generate(); + } + return _customers; + } + + private static void Generate() + { + _customers = Enumerable.Range(1, 5).Select(e => + new Customer + { + Id = e, + Name = "Customer #" + e, + Gender = e%2 == 0 ? Gender.Female : Gender.Male, + }).ToList(); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs new file mode 100644 index 000000000..6bd893aaf --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs @@ -0,0 +1,15 @@ +namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm +{ + public class Customer + { + public int Id { get; set; } + public string Name { get; set; } + public Gender Gender { get; set; } + } + + public enum Gender + { + Male = 1, + Female = 2 + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl index f690e2ab8..12587a158 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl @@ -118,7 +118,7 @@ public class Microsoft.AspNetCore.OData.ODataOptions { public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, Microsoft.AspNetCore.OData.Batch.ODataBatchHandler batchHandler) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions Count () - public Microsoft.AspNetCore.OData.ODataOptions EnableDependencyInjection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) + public Microsoft.AspNetCore.OData.ODataOptions ConfigureServiceCollection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions EnableQueryFeatures (params System.Nullable`1[[System.Int32]] maxTopValue) public Microsoft.AspNetCore.OData.ODataOptions Expand () public Microsoft.AspNetCore.OData.ODataOptions Filter () diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl index f690e2ab8..12587a158 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl @@ -118,7 +118,7 @@ public class Microsoft.AspNetCore.OData.ODataOptions { public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, Microsoft.AspNetCore.OData.Batch.ODataBatchHandler batchHandler) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions Count () - public Microsoft.AspNetCore.OData.ODataOptions EnableDependencyInjection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) + public Microsoft.AspNetCore.OData.ODataOptions ConfigureServiceCollection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) public Microsoft.AspNetCore.OData.ODataOptions EnableQueryFeatures (params System.Nullable`1[[System.Int32]] maxTopValue) public Microsoft.AspNetCore.OData.ODataOptions Expand () public Microsoft.AspNetCore.OData.ODataOptions Filter () From b6ea3c63db4f62eef6e633aad4b5f6c7d83a164c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Mitermite?= Date: Mon, 4 Apr 2022 11:37:12 +0200 Subject: [PATCH 4/7] Reorder using statements --- .../Extensions/HttpRequestExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs index 5bd3fb7ac..a4c10866b 100644 --- a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs @@ -5,6 +5,9 @@ // //------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.OData.Abstracts; using Microsoft.AspNetCore.OData.Common; @@ -15,9 +18,6 @@ using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; -using System; -using System.Collections.Generic; -using System.Linq; namespace Microsoft.AspNetCore.OData.Extensions { From 7ede7fa9dbd761b82fc6cec6d93b1c4de512fe80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Mitermite?= Date: Mon, 4 Apr 2022 15:56:09 +0200 Subject: [PATCH 5/7] Updates PublicApi bsl files --- .../PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl | 2 +- .../Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl index 12587a158..b346842e9 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net5.bsl @@ -117,8 +117,8 @@ public class Microsoft.AspNetCore.OData.ODataOptions { public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, Microsoft.AspNetCore.OData.Batch.ODataBatchHandler batchHandler) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) - public Microsoft.AspNetCore.OData.ODataOptions Count () public Microsoft.AspNetCore.OData.ODataOptions ConfigureServiceCollection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) + public Microsoft.AspNetCore.OData.ODataOptions Count () public Microsoft.AspNetCore.OData.ODataOptions EnableQueryFeatures (params System.Nullable`1[[System.Int32]] maxTopValue) public Microsoft.AspNetCore.OData.ODataOptions Expand () public Microsoft.AspNetCore.OData.ODataOptions Filter () diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl index 12587a158..b346842e9 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl @@ -117,8 +117,8 @@ public class Microsoft.AspNetCore.OData.ODataOptions { public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, Microsoft.AspNetCore.OData.Batch.ODataBatchHandler batchHandler) public Microsoft.AspNetCore.OData.ODataOptions AddRouteComponents (string routePrefix, Microsoft.OData.Edm.IEdmModel model, System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) - public Microsoft.AspNetCore.OData.ODataOptions Count () public Microsoft.AspNetCore.OData.ODataOptions ConfigureServiceCollection (System.Action`1[[Microsoft.Extensions.DependencyInjection.IServiceCollection]] configureServices) + public Microsoft.AspNetCore.OData.ODataOptions Count () public Microsoft.AspNetCore.OData.ODataOptions EnableQueryFeatures (params System.Nullable`1[[System.Int32]] maxTopValue) public Microsoft.AspNetCore.OData.ODataOptions Expand () public Microsoft.AspNetCore.OData.ODataOptions Filter () From e305e352207b61b572e68e43fc568ea3b2e1a197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Mitermite?= Date: Mon, 25 Apr 2022 18:03:01 +0200 Subject: [PATCH 6/7] Adds a using in EnableConfigureServiceCollectionTest method --- .../NonEdm/ConfigureServiceCollectionTest.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs index 492e7a102..a33165de9 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs @@ -3,8 +3,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.UriParser; using Newtonsoft.Json.Linq; -using System.Collections.Generic; -using System.Net; using System.Net.Http; using System.Threading.Tasks; using Xunit; @@ -34,9 +32,11 @@ protected static void UpdateConfigureServices(IServiceCollection services) [Fact] public async Task EnableConfigureServiceCollectionTest() { - HttpResponseMessage response = await CreateClient().SendAsync(new HttpRequestMessage(HttpMethod.Get, $"api/Customers?$filter=Gender eq 'MaLe'")); - var values = await response.Content.ReadAsObject(); - Assert.Equal(3, values.Count); + using (var response = await CreateClient().SendAsync(new HttpRequestMessage(HttpMethod.Get, $"api/Customers?$filter=Gender eq 'MaLe'"))) + { + var values = await response.Content.ReadAsObject(); + Assert.Equal(3, values.Count); + } } } } From 9c121bc303a2463ea2587df6300ca03fb6014bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Mitermite?= Date: Thu, 23 Feb 2023 15:33:53 +0100 Subject: [PATCH 7/7] Applies PR remarks --- .../Extensions/HttpRequestExtensions.cs | 2 + .../Microsoft.AspNetCore.OData.xml | 23 ++++-- .../ODataOptions.cs | 46 +++++++----- .../PublicAPI.Unshipped.txt | 3 +- .../NonEdm/ConfigureServiceCollectionTest.cs | 42 ----------- .../NonEdm/CustomersController.cs | 31 ++++++++ .../NonEdm/NonEdmDataModel.cs | 9 ++- ...NonEdmController.cs => NonEdmDbContext.cs} | 23 +++--- .../NonEdm/NonEdmTests.cs | 73 +++++++++++++++++++ 9 files changed, 168 insertions(+), 84 deletions(-) delete mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/CustomersController.cs rename test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/{NonEdmController.cs => NonEdmDbContext.cs} (60%) create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmTests.cs diff --git a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs index a4c10866b..91caeeb62 100644 --- a/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs @@ -299,6 +299,7 @@ public static IServiceProvider CreateRouteServices(this HttpRequest request, str // non-model scenario with dependency injection non enabled return null; } + IServiceProvider requestContainer = requestScope.ServiceProvider; request.ODataFeature().RequestScope = requestScope; @@ -345,6 +346,7 @@ private static IServiceScope CreateRequestScope(this HttpRequest request, string { return null; } + IServiceScope scope = rootContainer.GetRequiredService().CreateScope(); // Bind scoping request into the OData container. diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 557f2329f..f6ad8baf4 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -6154,19 +6154,26 @@ Gets the instance responsible for configuring the route template. - + + + Contains the OData instances and dependency injection containers for specific routes. + + DO NOT modify this dictionary yourself. Instead, use the 'AddRouteComponents()` methods for registering model instances. + + Configures service collection for non-EDM scenario - The OData version to be used. - The configuring action to add the services to the container. + The sub service configuration action. The current instance to enable fluent configuration. - + - Contains the OData instances and dependency injection containers for specific routes. + Configures service collection for non-EDM scenario - DO NOT modify this dictionary yourself. Instead, use the 'AddRouteComponents()` methods for registering model instances. + The OData version to be used. + The sub service configuration action. + The current instance to enable fluent configuration. @@ -6288,13 +6295,13 @@ Gets the query configurations. - + Build the container. + The Edm model. The OData version config. The setup config. - The Edm model. The built service provider. diff --git a/src/Microsoft.AspNetCore.OData/ODataOptions.cs b/src/Microsoft.AspNetCore.OData/ODataOptions.cs index 1713386e5..e0efdf8dd 100644 --- a/src/Microsoft.AspNetCore.OData/ODataOptions.cs +++ b/src/Microsoft.AspNetCore.OData/ODataOptions.cs @@ -29,6 +29,8 @@ namespace Microsoft.AspNetCore.OData public class ODataOptions { #region Settings + private IServiceProvider _serviceProvider; + /// /// Gets or sets the to use while parsing, specifically /// whether to recognize keys as segments or not. @@ -61,31 +63,39 @@ public class ODataOptions /// /// Gets the instance responsible for configuring the route template. /// - public ODataRouteOptions RouteOptions { get; } = new ODataRouteOptions(); + public ODataRouteOptions RouteOptions { get; } = new ODataRouteOptions(); + + #endregion - private IServiceProvider ServiceProvider { get; set; } + #region RouteComponents + + /// + /// Contains the OData instances and dependency injection containers for specific routes. + /// + /// DO NOT modify this dictionary yourself. Instead, use the 'AddRouteComponents()` methods for registering model instances. + public IDictionary RouteComponents { get; } = new Dictionary(); /// /// Configures service collection for non-EDM scenario /// - /// The OData version to be used. - /// The configuring action to add the services to the container. + /// The sub service configuration action. /// The current instance to enable fluent configuration. - public ODataOptions ConfigureServiceCollection(ODataVersion version, Action configureServices) + public ODataOptions AddRouteComponents(Action configureServices) { - ServiceProvider = BuildRouteContainer(version, configureServices); - return this; + return AddRouteComponents(ODataVersion.V4, configureServices); } - #endregion - - #region RouteComponents - /// - /// Contains the OData instances and dependency injection containers for specific routes. + /// Configures service collection for non-EDM scenario /// - /// DO NOT modify this dictionary yourself. Instead, use the 'AddRouteComponents()` methods for registering model instances. - public IDictionary RouteComponents { get; } = new Dictionary(); + /// The OData version to be used. + /// The sub service configuration action. + /// The current instance to enable fluent configuration. + public ODataOptions AddRouteComponents(ODataVersion version, Action configureServices) + { + _serviceProvider = BuildRouteContainer(null, version, configureServices); + return this; + } /// /// Adds an to the default route. @@ -171,7 +181,7 @@ public ODataOptions AddRouteComponents(string routePrefix, IEdmModel model, ODat } // Consider to use Lazy ? - IServiceProvider serviceProvider = BuildRouteContainer(version, configureServices, model); + IServiceProvider serviceProvider = BuildRouteContainer(model, version, configureServices); RouteComponents[sanitizedRoutePrefix] = (model, serviceProvider); return this; } @@ -185,7 +195,7 @@ public IServiceProvider GetRouteServices(string routePrefix) { if (routePrefix == null) { - return ServiceProvider; + return _serviceProvider; } string sanitizedRoutePrefix = SanitizeRoutePrefix(routePrefix); @@ -311,11 +321,11 @@ public ODataOptions SetMaxTop(int? maxTopValue) /// /// Build the container. /// + /// The Edm model. /// The OData version config. /// The setup config. - /// The Edm model. /// The built service provider. - private IServiceProvider BuildRouteContainer(ODataVersion version, Action setupAction, IEdmModel model = null) + private IServiceProvider BuildRouteContainer(IEdmModel model, ODataVersion version, Action setupAction) { ServiceCollection services = new ServiceCollection(); DefaultContainerBuilder builder = new DefaultContainerBuilder(); diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 972bf3966..a0b0d361e 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -648,6 +648,8 @@ Microsoft.AspNetCore.OData.ODataMvcOptionsSetup Microsoft.AspNetCore.OData.ODataMvcOptionsSetup.Configure(Microsoft.AspNetCore.Mvc.MvcOptions options) -> void Microsoft.AspNetCore.OData.ODataMvcOptionsSetup.ODataMvcOptionsSetup() -> void Microsoft.AspNetCore.OData.ODataOptions +Microsoft.AspNetCore.OData.ODataOptions.AddRouteComponents(System.Action configureServices) -> Microsoft.AspNetCore.OData.ODataOptions +Microsoft.AspNetCore.OData.ODataOptions.AddRouteComponents(Microsoft.OData.ODataVersion version, System.Action configureServices) -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.AddRouteComponents(Microsoft.OData.Edm.IEdmModel model) -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.AddRouteComponents(Microsoft.OData.Edm.IEdmModel model, Microsoft.AspNetCore.OData.Batch.ODataBatchHandler batchHandler) -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.AddRouteComponents(string routePrefix, Microsoft.OData.Edm.IEdmModel model) -> Microsoft.AspNetCore.OData.ODataOptions @@ -660,7 +662,6 @@ Microsoft.AspNetCore.OData.ODataOptions.EnableAttributeRouting.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableAttributeRouting.set -> void Microsoft.AspNetCore.OData.ODataOptions.EnableContinueOnErrorHeader.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableContinueOnErrorHeader.set -> void -Microsoft.AspNetCore.OData.ODataOptions.ConfigureServiceCollection(Microsoft.OData.ODataVersion version, System.Action configureServices) -> Microsoft.AspNetCore.OData.ODataOptions Microsoft.AspNetCore.OData.ODataOptions.EnableNoDollarQueryOptions.get -> bool Microsoft.AspNetCore.OData.ODataOptions.EnableNoDollarQueryOptions.set -> void Microsoft.AspNetCore.OData.ODataOptions.EnableQueryFeatures(int? maxTopValue = null) -> Microsoft.AspNetCore.OData.ODataOptions diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs deleted file mode 100644 index a33165de9..000000000 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/ConfigureServiceCollectionTest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.AspNetCore.OData.E2E.Tests.Extensions; -using Microsoft.AspNetCore.OData.TestCommon; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OData.UriParser; -using Newtonsoft.Json.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm -{ - public class ConfigureServiceCollectionTest : WebApiTestBase - { - public ConfigureServiceCollectionTest(WebApiTestFixture fixture) - : base(fixture) - { - } - - protected static void UpdateConfigureServices(IServiceCollection services) - { - services.ConfigureControllers(typeof(CustomersController)); - services.AddControllers().AddOData(opt => - { - opt.EnableQueryFeatures(); - opt.ConfigureServiceCollection(services => - { - services.AddSingleton(sp => new StringAsEnumResolver() { EnableCaseInsensitive = true }); - }); - }); - } - - [Fact] - public async Task EnableConfigureServiceCollectionTest() - { - using (var response = await CreateClient().SendAsync(new HttpRequestMessage(HttpMethod.Get, $"api/Customers?$filter=Gender eq 'MaLe'"))) - { - var values = await response.Content.ReadAsObject(); - Assert.Equal(3, values.Count); - } - } - } -} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/CustomersController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/CustomersController.cs new file mode 100644 index 000000000..589e04bb0 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/CustomersController.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Query; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm +{ + [ApiController] + [Route("api/[controller]")] + public class CustomersController : ControllerBase + { + [HttpGet] + public IActionResult Get(ODataQueryOptions options) + { + return Ok(options.ApplyTo(NonEdmDbContext.GetCustomers().AsQueryable())); + } + + [HttpGet("WithEnableQueryAttribute")] + [EnableQuery] + public IActionResult GetWithEnableQueryAttribute() + { + return Ok(NonEdmDbContext.GetCustomers()); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs index 6bd893aaf..0e23f85e6 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDataModel.cs @@ -1,4 +1,11 @@ -namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm { public class Customer { diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDbContext.cs similarity index 60% rename from test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmController.cs rename to test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDbContext.cs index 718254a11..b49537984 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmController.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmDbContext.cs @@ -1,21 +1,15 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.OData.Query; +//----------------------------------------------------------------------------- +// +// 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; namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm { - [ApiController] - [Route("api/[controller]")] - public class CustomersController : ControllerBase - { - [HttpGet] - public IActionResult Get(ODataQueryOptions options) - { - return Ok(options.ApplyTo(NonEdmDbContext.GetCustomers().AsQueryable())); - } - } - public class NonEdmDbContext { private static IList _customers; @@ -31,7 +25,7 @@ public static IList GetCustomers() private static void Generate() { - _customers = Enumerable.Range(1, 5).Select(e => + _customers = Enumerable.Range(1, 10).Select(e => new Customer { Id = e, @@ -40,4 +34,5 @@ private static void Generate() }).ToList(); } } + } diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmTests.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmTests.cs new file mode 100644 index 000000000..864a30ec2 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/NonEdm/NonEdmTests.cs @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.OData.E2E.Tests.Extensions; +using Microsoft.AspNetCore.OData.TestCommon; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OData.UriParser; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm +{ + public class NonEdmTests : WebApiTestBase + { + public NonEdmTests(WebApiTestFixture fixture) + : base(fixture) + { + } + + protected static void UpdateConfigureServices(IServiceCollection services) + { + services.ConfigureControllers(typeof(CustomersController)); + services.AddControllers().AddOData(opt => + { + opt.EnableQueryFeatures(); + opt.AddRouteComponents(services => + { + services.AddSingleton(sp => new StringAsEnumResolver() { EnableCaseInsensitive = true }); + }); + }); + } + + [Fact] + public async Task NonEdmFilterByEnumString() + { + Assert.Equal(5, (await GetResponse("$filter=Gender eq 'MaLe'")).Length); + } + + [Fact] + public async Task NonEdmFilterByEnumStringWithEnableQueryAttribute() + { + Assert.Equal(5, (await GetResponse("$filter=Gender eq 'MaLe'", "WithEnableQueryAttribute")).Length); + } + + [Fact] + public async Task NonEdmSumFilteredByEnumString() + { + var response = await GetResponse("$apply=filter(Gender eq 'female')/aggregate(Id with sum as Sum)"); + Assert.Equal(30, response.Single()["Sum"].Value()); + } + + [Fact] + public async Task NonEdmSelectTopFilteredByEnumString() + { + var response = await GetResponse("$filter(Gender eq 'female')&$orderby=Id desc&$select=Name&$top=1&$skip=1"); + Assert.Equal("Customer #9", response.Single().Name); + } + + private async Task GetResponse(string queryOptions, string method = null) + { + using var response = await CreateClient().SendAsync( + new HttpRequestMessage(HttpMethod.Get, $"api/Customers/{method ?? string.Empty}?{queryOptions}")); + return await response.Content.ReadAsObject(); + } + } +}