Skip to content
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

Fixed Scoped Service Handling #1066

Merged
merged 3 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ jobs:
pull-request:
working_directory: /work
docker:
- image: chillicream/dotnet-build:3.0
- image: chillicream/dotnet-build:4.0
steps:
- checkout
- run:
name: Check that Templates Compile
command: dotnet cake -target=TemplatesCompile
command: |
export PATH="$PATH:/root/.dotnet/tools"
dotnet cake -target=TemplatesCompile
- run:
name: Pull Request Build
command: dotnet cake -target=CoreTests
command: |
export PATH="$PATH:/root/.dotnet/tools"
dotnet cake -target=CoreTests
- store_test_results:
path: /work/testoutput
- store_artifacts:
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "2.2.103"
"version": "2.2.402"
}
}
2 changes: 0 additions & 2 deletions src/Core/Core/Execution/QueryExecutionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ public void Populate(IServiceCollection services, bool lazyExecutor)
() => new QueryExecutor
(
sp.GetRequiredService<ISchema>(),
sp,
Compile(_middlewareComponents),
Compile(_fieldMiddlewareComponents)
)
Expand All @@ -99,7 +98,6 @@ public void Populate(IServiceCollection services, bool lazyExecutor)
return new QueryExecutor
(
sp.GetRequiredService<ISchema>(),
sp,
Compile(_middlewareComponents),
Compile(_fieldMiddlewareComponents)
);
Expand Down
55 changes: 44 additions & 11 deletions src/Core/Core/Execution/QueryExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,19 @@ public QueryExecutor(
IServiceProvider applicationServices,
QueryDelegate queryDelegate,
FieldMiddleware fieldMiddleware)
: this(schema, queryDelegate, fieldMiddleware)
{
Schema = schema
?? throw new ArgumentNullException(nameof(schema));
_applicationServices = applicationServices
?? throw new ArgumentNullException(nameof(applicationServices));
}

public QueryExecutor(
ISchema schema,
QueryDelegate queryDelegate,
FieldMiddleware fieldMiddleware)
{
Schema = schema
?? throw new ArgumentNullException(nameof(schema));
_queryDelegate = queryDelegate
?? throw new ArgumentNullException(nameof(queryDelegate));

Expand Down Expand Up @@ -92,20 +100,33 @@ private async Task<IExecutionResult> ExecuteMiddlewareAsync(
private IRequestServiceScope CreateServiceScope(
IServiceProvider requestServices)
{
IServiceScope serviceScope = _applicationServices.CreateScope();
IServiceProvider services = requestServices ?? Schema.Services;

if (services == null)
if (_applicationServices is null)
{
return new RequestServiceScope(
serviceScope.ServiceProvider,
serviceScope);
CreateRequestServices(requestServices),
Disposable.Instance);
}
else
{
IServiceScope serviceScope = _applicationServices.CreateScope();
IServiceProvider services = requestServices ?? Schema.Services;

if (services == null)
{
return new RequestServiceScope(
serviceScope.ServiceProvider,
serviceScope);
}

services = serviceScope.ServiceProvider.Include(services);
return new RequestServiceScope(services, serviceScope);
services = serviceScope.ServiceProvider.Include(services);
return new RequestServiceScope(services, serviceScope);
}
}

private IServiceProvider CreateRequestServices(
IServiceProvider requestServices) =>
requestServices ?? Schema.Services;

public void Dispose()
{
Dispose(true);
Expand All @@ -116,12 +137,24 @@ protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing && _applicationServices is IDisposable d)
if (disposing
&& _applicationServices != null
&& _applicationServices is IDisposable d)
{
d.Dispose();
}
_disposed = true;
}
}

private class Disposable
: IDisposable
{
public void Dispose()
{
}

public static Disposable Instance { get; } = new Disposable();
}
}
}
20 changes: 9 additions & 11 deletions src/Server/AspNetCore.HttpPost/HttpPostMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,10 @@ await _streamSerializer.SerializeAsync(
.ConfigureAwait(false);
}

private async Task<IReadOnlyList<IReadOnlyQueryRequest>>
BuildBatchRequestAsync(
HttpContext context,
IServiceProvider services,
IReadOnlyList<GraphQLRequest> batch)
private async Task<IReadOnlyList<IReadOnlyQueryRequest>> BuildBatchRequestAsync(
HttpContext context,
IServiceProvider services,
IReadOnlyList<GraphQLRequest> batch)
{
var queryBatch = new IReadOnlyQueryRequest[batch.Count];

Expand All @@ -248,12 +247,11 @@ private async Task<IReadOnlyList<IReadOnlyQueryRequest>>
return queryBatch;
}

private async Task<IReadOnlyList<IReadOnlyQueryRequest>>
BuildBatchRequestAsync(
HttpContext context,
IServiceProvider services,
GraphQLRequest request,
IReadOnlyList<string> operationNames)
private async Task<IReadOnlyList<IReadOnlyQueryRequest>> BuildBatchRequestAsync(
HttpContext context,
IServiceProvider services,
GraphQLRequest request,
IReadOnlyList<string> operationNames)
{
var queryBatch = new IReadOnlyQueryRequest[operationNames.Count];

Expand Down
61 changes: 60 additions & 1 deletion src/Server/AspNetCore.Tests/PostMiddlewareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using HotChocolate;
using HotChocolate.Types;
using HotChocolate.AspNetCore.Tests.Utilities;
using HotChocolate.Language;
using Microsoft.AspNetCore.TestHost;
using Snapshooter;
using Snapshooter.Xunit;
using Xunit;
Expand Down Expand Up @@ -802,5 +806,60 @@ await server.SendPostRequestAsync(
byte[] json = await message.Content.ReadAsByteArrayAsync();
Utf8GraphQLRequestParser.ParseJson(json).MatchSnapshot();
}

[Fact]
public async Task HttpPost_Ensure_Scoped_Services_Work()
{
// arrange
TestServer server = ServerFactory.Create(
services =>
{
services.AddScoped<ScopedService>();
services.AddGraphQL(SchemaBuilder.New()
.AddQueryType(c => c
.Name("Query")
.Field("foo")
.Resolver(ctx =>
{
ScopedService service = ctx.Service<ScopedService>();
service.Increase();
return service.Count;
})));
},
app => app
.Use(next => ctx =>
{
ScopedService service = ctx.RequestServices.GetService<ScopedService>();
service.Increase();
return next(ctx);
})
.UseGraphQL());

var request =
@"
{
foo
}
";
var contentType = "application/graphql";

// act
HttpResponseMessage message =
await server.SendPostRequestAsync(request, contentType, null);

// assert
ClientQueryResult result = await DeserializeAsync(message);
result.MatchSnapshot();
}

public class ScopedService
{
public int Count { get; private set; }

public void Increase()
{
Count += 1;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"Data": {
"foo": 2
},
"Errors": null,
"Extensions": null
}