-
Notifications
You must be signed in to change notification settings - Fork 164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds dependency injection support in non-model scenario #439
base: release-8.x
Are you sure you want to change the base?
Changes from 7 commits
f7a6048
c8013bd
4a09031
f92320a
b6ea3c6
7ede7fa
e305e35
d8a675a
9c121bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -341,14 +340,15 @@ private static IServiceScope CreateRequestScope(this HttpRequest request, string | |
{ | ||
ODataOptions options = request.ODataOptions(); | ||
|
||
IServiceProvider rootContainer = options.GetRouteServices(routePrefix); | ||
IServiceScope scope = rootContainer.GetRequiredService<IServiceScopeFactory>().CreateScope(); | ||
|
||
// Bind scoping request into the OData container. | ||
if (!string.IsNullOrEmpty(routePrefix)) | ||
IServiceProvider rootContainer = options?.GetRouteServices(routePrefix); | ||
if (rootContainer == null) | ||
{ | ||
scope.ServiceProvider.GetRequiredService<HttpRequestScope>().HttpRequest = request; | ||
return null; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. insert whiteline after {} block |
||
IServiceScope scope = rootContainer.GetRequiredService<IServiceScopeFactory>().CreateScope(); | ||
|
||
// Bind scoping request into the OData container. | ||
scope.ServiceProvider.GetRequiredService<HttpRequestScope>().HttpRequest = request; | ||
kakone marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return scope; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,6 +62,19 @@ public class ODataOptions | |
/// </summary> | ||
public ODataRouteOptions RouteOptions { get; } = new ODataRouteOptions(); | ||
|
||
private IServiceProvider ServiceProvider { get; set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make it a field, it seems we don't need a property |
||
|
||
/// <summary> | ||
/// Configures service collection for non-EDM scenario | ||
/// </summary> | ||
/// <param name="configureServices">The configuring action to add the services to the container.</param> | ||
/// <returns>The current <see cref="ODataOptions"/> instance to enable fluent configuration.</returns> | ||
public ODataOptions ConfigureServiceCollection(Action<IServiceCollection> configureServices) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
ServiceProvider = BuildContainer(configureServices); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return this; | ||
} | ||
|
||
#endregion | ||
|
||
#region RouteComponents | ||
|
@@ -144,7 +157,7 @@ public ODataOptions AddRouteComponents(string routePrefix, IEdmModel model, Acti | |
|
||
|
||
// Consider to use Lazy<IServiceProvider> ? | ||
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) | |
/// <summary> | ||
/// Build the container. | ||
/// </summary> | ||
/// <param name="model">The Edm model.</param> | ||
/// <param name="setupAction">The setup config.</param> | ||
/// <param name="model">The Edm model.</param> | ||
/// <returns>The built service provider.</returns> | ||
private IServiceProvider BuildRouteContainer(IEdmModel model, Action<IServiceCollection> setupAction) | ||
private IServiceProvider BuildContainer(Action<IServiceCollection> setupAction, IEdmModel model = null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
Contract.Assert(model != null); | ||
|
||
ServiceCollection services = new ServiceCollection(); | ||
DefaultContainerBuilder builder = new DefaultContainerBuilder(); | ||
|
||
|
@@ -311,10 +322,13 @@ private IServiceProvider BuildRouteContainer(IEdmModel model, Action<IServiceCol | |
EnableNoDollarQueryOptions = EnableNoDollarQueryOptions // retrieve it from global setting | ||
}); | ||
|
||
// 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); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.Net.Http; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm | ||
{ | ||
public class ConfigureServiceCollectionTest : WebApiTestBase<ConfigureServiceCollectionTest> | ||
{ | ||
public ConfigureServiceCollectionTest(WebApiTestFixture<ConfigureServiceCollectionTest> fixture) | ||
: base(fixture) | ||
{ | ||
} | ||
|
||
protected static void UpdateConfigureServices(IServiceCollection services) | ||
{ | ||
services.ConfigureControllers(typeof(CustomersController)); | ||
services.AddControllers().AddOData(opt => | ||
{ | ||
opt.EnableQueryFeatures(); | ||
opt.ConfigureServiceCollection(services => | ||
{ | ||
services.AddSingleton<ODataUriResolver>(sp => new StringAsEnumResolver() { EnableCaseInsensitive = true }); | ||
}); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public async Task EnableConfigureServiceCollectionTest() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
using (var response = await CreateClient().SendAsync(new HttpRequestMessage(HttpMethod.Get, $"api/Customers?$filter=Gender eq 'MaLe'"))) | ||
{ | ||
var values = await response.Content.ReadAsObject<JArray>(); | ||
Assert.Equal(3, values.Count); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.OData.Query; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copy right head |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move using System.* ahead |
||
namespace Microsoft.AspNetCore.OData.E2E.Tests.NonEdm | ||
{ | ||
[ApiController] | ||
[Route("api/[controller]")] | ||
public class CustomersController : ControllerBase | ||
{ | ||
[HttpGet] | ||
public IActionResult Get(ODataQueryOptions<Customer> options) | ||
{ | ||
return Ok(options.ApplyTo(NonEdmDbContext.GetCustomers().AsQueryable())); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a test using [EnableQuery] attribute |
||
} | ||
|
||
public class NonEdmDbContext | ||
{ | ||
private static IList<Customer> _customers; | ||
|
||
public static IList<Customer> 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(); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. put it into a new file |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
insert whiteline