Skip to content

Commit 1deed7a

Browse files
authored
Better service resolution by leveraging RequestServices (#1090)
1 parent 6d1f1e8 commit 1deed7a

File tree

14 files changed

+67
-122
lines changed

14 files changed

+67
-122
lines changed

docs/configuration.md

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ A delegate that resolves the DbContext.
7272
```cs
7373
namespace GraphQL.EntityFramework;
7474

75-
public delegate TDbContext ResolveDbContext<out TDbContext>(object userContext)
75+
public delegate TDbContext ResolveDbContext<out TDbContext>(object userContext, IServiceProvider? requestServices)
7676
where TDbContext : DbContext;
7777
```
7878
<sup><a href='/src/GraphQL.EntityFramework/GraphApi/ResolveDbContext.cs#L1-L4' title='Snippet source file'>snippet source</a> | <a href='#snippet-ResolveDbContext.cs' title='Start of snippet'>anchor</a></sup>
@@ -298,23 +298,6 @@ public class GraphQlController(ISchema schema, IDocumentExecuter executer) :
298298
Multiple different DbContext types can be registered and used.
299299

300300

301-
### UserContext
302-
303-
A user context that exposes both types.
304-
305-
<!-- snippet: MultiUserContext -->
306-
<a id='snippet-MultiUserContext'></a>
307-
```cs
308-
public class UserContext(DbContext1 context1, DbContext2 context2) : Dictionary<string, object?>
309-
{
310-
public readonly DbContext1 DbContext1 = context1;
311-
public readonly DbContext2 DbContext2 = context2;
312-
}
313-
```
314-
<sup><a href='/src/Tests/MultiContextTests/MultiContextTests.cs#L80-L86' title='Snippet source file'>snippet source</a> | <a href='#snippet-MultiUserContext' title='Start of snippet'>anchor</a></sup>
315-
<!-- endSnippet -->
316-
317-
318301
### Register in container
319302

320303
Register both DbContext types in the container and include how those instance can be extracted from the GraphQL context:
@@ -324,10 +307,10 @@ Register both DbContext types in the container and include how those instance ca
324307
```cs
325308
EfGraphQLConventions.RegisterInContainer(
326309
services,
327-
userContext => ((UserContext) userContext).DbContext1);
310+
(_, requestServices) => requestServices!.GetRequiredService<DbContext1>());
328311
EfGraphQLConventions.RegisterInContainer(
329312
services,
330-
userContext => ((UserContext) userContext).DbContext2);
313+
(_, requestServices) => requestServices!.GetRequiredService<DbContext2>());
331314
```
332315
<sup><a href='/src/Tests/MultiContextTests/MultiContextTests.cs#L49-L58' title='Snippet source file'>snippet source</a> | <a href='#snippet-RegisterMultipleInContainer' title='Start of snippet'>anchor</a></sup>
333316
<!-- endSnippet -->
@@ -345,7 +328,7 @@ var executionOptions = new ExecutionOptions
345328
{
346329
Schema = schema,
347330
Query = query,
348-
UserContext = new UserContext(dbContext1, dbContext2)
331+
RequestServices = provider,
349332
};
350333
```
351334
<sup><a href='/src/Tests/MultiContextTests/MultiContextTests.cs#L64-L73' title='Snippet source file'>snippet source</a> | <a href='#snippet-MultiExecutionOptions' title='Start of snippet'>anchor</a></sup>
@@ -369,39 +352,23 @@ public class MultiContextQuery :
369352
efGraphQlService1.AddSingleField(
370353
graph: this,
371354
name: "entity1",
372-
resolve: context =>
373-
{
374-
var userContext = (UserContext) context.UserContext;
375-
return userContext.DbContext1.Entities;
376-
});
355+
resolve: _ => _.DbContext.Entities);
377356
efGraphQlService1.AddFirstField(
378357
graph: this,
379358
name: "entity1First",
380-
resolve: context =>
381-
{
382-
var userContext = (UserContext) context.UserContext;
383-
return userContext.DbContext1.Entities;
384-
});
359+
resolve: _ => _.DbContext.Entities);
385360
efGraphQlService2.AddSingleField(
386361
graph: this,
387362
name: "entity2",
388-
resolve: context =>
389-
{
390-
var userContext = (UserContext) context.UserContext;
391-
return userContext.DbContext2.Entities;
392-
});
363+
resolve: _ => _.DbContext.Entities);
393364
efGraphQlService2.AddFirstField(
394365
graph: this,
395366
name: "entity2First",
396-
resolve: context =>
397-
{
398-
var userContext = (UserContext) context.UserContext;
399-
return userContext.DbContext2.Entities;
400-
});
367+
resolve: _ => _.DbContext.Entities);
401368
}
402369
}
403370
```
404-
<sup><a href='/src/Tests/MultiContextTests/MultiContextQuery.cs#L1-L41' title='Snippet source file'>snippet source</a> | <a href='#snippet-MultiContextQuery.cs' title='Start of snippet'>anchor</a></sup>
371+
<sup><a href='/src/Tests/MultiContextTests/MultiContextQuery.cs#L1-L25' title='Snippet source file'>snippet source</a> | <a href='#snippet-MultiContextQuery.cs' title='Start of snippet'>anchor</a></sup>
405372
<!-- endSnippet -->
406373

407374

docs/mdsource/configuration.source.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,6 @@ snippet: GraphQlController
139139
Multiple different DbContext types can be registered and used.
140140

141141

142-
### UserContext
143-
144-
A user context that exposes both types.
145-
146-
snippet: MultiUserContext
147-
148-
149142
### Register in container
150143

151144
Register both DbContext types in the container and include how those instance can be extracted from the GraphQL context:

src/GraphQL.EntityFramework/EfGraphQLConventions.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@ public static void RegisterInContainer<TDbContext>(
2828
RegisterScalarsAndArgs(services);
2929
services.AddHttpContextAccessor();
3030
services.AddTransient<HttpContextCapture>();
31-
services.AddSingleton(
32-
provider => Build(resolveDbContext, model, resolveFilters, provider, disableTracking, disableAsync));
33-
services.AddSingleton<IEfGraphQLService<TDbContext>>(
34-
provider => provider.GetRequiredService<EfGraphQLService<TDbContext>>());
31+
services.AddSingleton(provider => Build(resolveDbContext, model, resolveFilters, provider, disableTracking, disableAsync));
32+
services.AddSingleton<IEfGraphQLService<TDbContext>>(provider => provider.GetRequiredService<EfGraphQLService<TDbContext>>());
3533
}
3634

3735
static EfGraphQLService<TDbContext> Build<TDbContext>(
@@ -45,7 +43,7 @@ static EfGraphQLService<TDbContext> Build<TDbContext>(
4543
{
4644
model ??= ResolveModel<TDbContext>(provider);
4745
filters ??= provider.GetService<ResolveFilters<TDbContext>>();
48-
dbContextResolver ??= _ => DbContextFromProvider<TDbContext>(provider);
46+
dbContextResolver ??= (_, requestServices) => DbContextFromProvider<TDbContext>(provider, requestServices);
4947

5048
return new(
5149
model,
@@ -55,9 +53,16 @@ static EfGraphQLService<TDbContext> Build<TDbContext>(
5553
disableAsync);
5654
}
5755

58-
static TDbContext DbContextFromProvider<TDbContext>(IServiceProvider provider)
56+
static TDbContext DbContextFromProvider<TDbContext>(IServiceProvider provider, IServiceProvider? requestServices)
5957
where TDbContext : DbContext
6058
{
59+
var dataFromRequestServices = requestServices?
60+
.GetService<TDbContext>();
61+
if (dataFromRequestServices is not null)
62+
{
63+
return dataFromRequestServices;
64+
}
65+
6166
var dataFromHttpContext = provider.GetService<HttpContextCapture>()?
6267
.HttpContextAccessor
6368
.HttpContext?

src/GraphQL.EntityFramework/GraphApi/EfGraphQLService.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,13 @@ ResolveEfFieldContext<TDbContext, TSource> BuildContext<TSource>(
6565
User = context.User
6666
};
6767

68-
public TDbContext ResolveDbContext(IResolveFieldContext context) =>
69-
resolveDbContext(context.UserContext);
68+
public TDbContext ResolveDbContext(IResolveFieldContext fieldContext)
69+
{
70+
var userContext = fieldContext.UserContext;
71+
var executionContext = fieldContext.ExecutionContext;
72+
var requestServices = executionContext.RequestServices ?? executionContext.ExecutionOptions.RequestServices;
73+
return resolveDbContext(userContext, requestServices);
74+
}
7075

7176
Filters<TDbContext>? ResolveFilter<TSource>(IResolveFieldContext<TSource> context) =>
7277
resolveFilters?.Invoke(context.UserContext);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
namespace GraphQL.EntityFramework;
22

3-
public delegate TDbContext ResolveDbContext<out TDbContext>(object userContext)
3+
public delegate TDbContext ResolveDbContext<out TDbContext>(object userContext, IServiceProvider? requestServices)
44
where TDbContext : DbContext;

src/SampleWeb/Subscription.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@
101101
// {
102102
// Document = document,
103103
// Schema = schema,
104-
// UserContext = new UserContext(dbContext),
105104
// Variables = variableValues,
106105
// Fragments = document.Fragments,
107106
// CancellationToken = token,
@@ -141,7 +140,6 @@
141140
// Document = context.Document,
142141
// Fragments = context.Fragments,
143142
// RootValue = context.RootValue,
144-
// UserContext = context.UserContext,
145143
// Operation = context.Operation,
146144
// Variables = context.Variables,
147145
// CancellationToken = context.CancellationToken,

src/SampleWeb/UserContext.cs

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/Tests/DependencyResolutionTests/DependencyTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ public async Task ExplicitModel()
2424
services.AddSingleton<DependencySchema>();
2525
EfGraphQLConventions.RegisterInContainer(
2626
services,
27-
userContext => ((UserContextSingleDb<DependencyDbContext>) userContext).DbContext,
27+
(_, _) => dbContext,
2828
sqlInstance.Model);
2929
await using var provider = services.BuildServiceProvider();
3030
using var schema = provider.GetRequiredService<DependencySchema>();
3131
var executionOptions = new ExecutionOptions
3232
{
3333
Schema = schema,
3434
Query = query,
35-
UserContext = new UserContextSingleDb<DependencyDbContext>(dbContext),
36-
Variables = null
35+
Variables = null,
36+
RequestServices = provider
3737
};
3838

3939
await ExecutionResultData(executionOptions);
@@ -50,15 +50,15 @@ public async Task ScopedDbContext()
5050

5151
EfGraphQLConventions.RegisterInContainer(
5252
services,
53-
userContext => ((UserContextSingleDb<DependencyDbContext>) userContext).DbContext);
53+
(_, requestServices) => requestServices!.GetRequiredService<DependencyDbContext>());
5454
await using var provider = services.BuildServiceProvider();
5555
using var schema = new DependencySchema(provider);
5656
var executionOptions = new ExecutionOptions
5757
{
5858
Schema = schema,
5959
Query = query,
60-
UserContext = new UserContextSingleDb<DependencyDbContext>(dbContext),
61-
Variables = null
60+
Variables = null,
61+
RequestServices = provider
6262
};
6363

6464
await ExecutionResultData(executionOptions);
@@ -75,15 +75,15 @@ public async Task TransientDbContext()
7575

7676
EfGraphQLConventions.RegisterInContainer(
7777
services,
78-
userContext => ((UserContextSingleDb<DependencyDbContext>) userContext).DbContext);
78+
(_, requestServices) => requestServices!.GetRequiredService<DependencyDbContext>());
7979
await using var provider = services.BuildServiceProvider();
8080
using var schema = new DependencySchema(provider);
8181
var options = new ExecutionOptions
8282
{
8383
Schema = schema,
8484
Query = query,
85-
UserContext = new UserContextSingleDb<DependencyDbContext>(dbContext),
86-
Variables = null
85+
Variables = null,
86+
RequestServices = provider
8787
};
8888

8989
await ExecutionResultData(options);
@@ -100,15 +100,15 @@ public async Task SingletonDbContext()
100100

101101
EfGraphQLConventions.RegisterInContainer(
102102
services,
103-
userContext => ((UserContextSingleDb<DependencyDbContext>) userContext).DbContext);
103+
(_, requestServices) => requestServices!.GetRequiredService<DependencyDbContext>());
104104
await using var provider = services.BuildServiceProvider();
105105
using var schema = new DependencySchema(provider);
106106
var options = new ExecutionOptions
107107
{
108108
Schema = schema,
109109
Query = query,
110-
UserContext = new UserContextSingleDb<DependencyDbContext>(dbContext),
111-
Variables = null
110+
Variables = null,
111+
RequestServices = provider
112112
};
113113

114114
await ExecutionResultData(options);

src/Tests/IntegrationTests/IntegrationTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public async Task SchemaPrint()
4040
services.AddSingleton(type);
4141
}
4242

43-
EfGraphQLConventions.RegisterInContainer(services, _ => dbContext, dbContext.Model);
43+
EfGraphQLConventions.RegisterInContainer(services, (_, _) => dbContext, dbContext.Model);
4444
await using var provider = services.BuildServiceProvider();
4545
using var schema = new Schema(provider);
4646

src/Tests/IntegrationTests/QueryExecutor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public static async Task<string> ExecuteQuery<TDbContext>(
1212
{
1313
EfGraphQLConventions.RegisterInContainer(
1414
services,
15-
_ => data,
15+
(_, _) => data,
1616
data.Model,
1717
_ => filters,
1818
disableTracking,
@@ -26,8 +26,8 @@ public static async Task<string> ExecuteQuery<TDbContext>(
2626
Schema = schema,
2727
Query = query,
2828
ThrowOnUnhandledException = true,
29-
UserContext = new UserContextSingleDb<TDbContext>(data),
3029
Variables = inputs,
30+
RequestServices = provider,
3131
};
3232

3333
var result = await executer.ExecuteWithErrorCheck(options);

src/Tests/Mapping/MappingTests.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
public class MappingTests
1+
using ExecutionContext = GraphQL.Execution.ExecutionContext;
2+
3+
public class MappingTests
24
{
35
static SqlInstance<MappingContext> sqlInstance;
46

@@ -43,22 +45,32 @@ public async Task Resolve()
4345
await database.AddDataUntracked(child, parent);
4446
var services = new ServiceCollection();
4547
services.AddSingleton<MappingQuery>();
46-
EfGraphQLConventions.RegisterInContainer(services,_ => database.NewDbContext(), model:sqlInstance.Model);
48+
EfGraphQLConventions.RegisterInContainer(services, (_, _) => database.NewDbContext(), model: sqlInstance.Model);
4749
await using var provider = services.BuildServiceProvider();
4850
var mappingQuery = provider.GetRequiredService<MappingQuery>();
4951

52+
var fieldContext = new ResolveFieldContext
53+
{
54+
ExecutionContext = new ExecutionContext
55+
{
56+
RequestServices = provider
57+
}
58+
};
5059
var resolve = await mappingQuery.Fields
5160
.Single(_ => _.Name == "children")
5261
.Resolver!
53-
.ResolveAsync(new ResolveFieldContext());
62+
.ResolveAsync(fieldContext);
5463
await Verify(resolve);
5564
}
5665

5766
[Fact]
5867
public async Task PropertyToObject()
5968
{
6069
var expression = Mapper<MappingContext>.PropertyToObject<MappingParent>("Property");
61-
var result = expression.Compile()(new() {Property = "value"});
70+
var result = expression.Compile()(new()
71+
{
72+
Property = "value"
73+
});
6274
await Verify(
6375
new
6476
{

src/Tests/MultiContextTests/MultiContextQuery.cs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,18 @@ public MultiContextQuery(
88
efGraphQlService1.AddSingleField(
99
graph: this,
1010
name: "entity1",
11-
resolve: context =>
12-
{
13-
var userContext = (UserContext) context.UserContext;
14-
return userContext.DbContext1.Entities;
15-
});
11+
resolve: _ => _.DbContext.Entities);
1612
efGraphQlService1.AddFirstField(
1713
graph: this,
1814
name: "entity1First",
19-
resolve: context =>
20-
{
21-
var userContext = (UserContext) context.UserContext;
22-
return userContext.DbContext1.Entities;
23-
});
15+
resolve: _ => _.DbContext.Entities);
2416
efGraphQlService2.AddSingleField(
2517
graph: this,
2618
name: "entity2",
27-
resolve: context =>
28-
{
29-
var userContext = (UserContext) context.UserContext;
30-
return userContext.DbContext2.Entities;
31-
});
19+
resolve: _ => _.DbContext.Entities);
3220
efGraphQlService2.AddFirstField(
3321
graph: this,
3422
name: "entity2First",
35-
resolve: context =>
36-
{
37-
var userContext = (UserContext) context.UserContext;
38-
return userContext.DbContext2.Entities;
39-
});
23+
resolve: _ => _.DbContext.Entities);
4024
}
4125
}

0 commit comments

Comments
 (0)