diff --git a/docs/configuration.md b/docs/configuration.md index b20865339..e6f15340f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -72,7 +72,7 @@ A delegate that resolves the DbContext. ```cs namespace GraphQL.EntityFramework; -public delegate TDbContext ResolveDbContext(object userContext) +public delegate TDbContext ResolveDbContext(object userContext, IServiceProvider? requestServices) where TDbContext : DbContext; ``` snippet source | anchor @@ -298,23 +298,6 @@ public class GraphQlController(ISchema schema, IDocumentExecuter executer) : Multiple different DbContext types can be registered and used. -### UserContext - -A user context that exposes both types. - - - -```cs -public class UserContext(DbContext1 context1, DbContext2 context2) : Dictionary -{ - public readonly DbContext1 DbContext1 = context1; - public readonly DbContext2 DbContext2 = context2; -} -``` -snippet source | anchor - - - ### Register in container 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 ```cs EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContext) userContext).DbContext1); + (_, requestServices) => requestServices!.GetRequiredService()); EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContext) userContext).DbContext2); + (_, requestServices) => requestServices!.GetRequiredService()); ``` snippet source | anchor @@ -345,7 +328,7 @@ var executionOptions = new ExecutionOptions { Schema = schema, Query = query, - UserContext = new UserContext(dbContext1, dbContext2) + RequestServices = provider, }; ``` snippet source | anchor @@ -369,39 +352,23 @@ public class MultiContextQuery : efGraphQlService1.AddSingleField( graph: this, name: "entity1", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext1.Entities; - }); + resolve: _ => _.DbContext.Entities); efGraphQlService1.AddFirstField( graph: this, name: "entity1First", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext1.Entities; - }); + resolve: _ => _.DbContext.Entities); efGraphQlService2.AddSingleField( graph: this, name: "entity2", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext2.Entities; - }); + resolve: _ => _.DbContext.Entities); efGraphQlService2.AddFirstField( graph: this, name: "entity2First", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext2.Entities; - }); + resolve: _ => _.DbContext.Entities); } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/mdsource/configuration.source.md b/docs/mdsource/configuration.source.md index 53f67b93f..a4559fe06 100644 --- a/docs/mdsource/configuration.source.md +++ b/docs/mdsource/configuration.source.md @@ -139,13 +139,6 @@ snippet: GraphQlController Multiple different DbContext types can be registered and used. -### UserContext - -A user context that exposes both types. - -snippet: MultiUserContext - - ### Register in container Register both DbContext types in the container and include how those instance can be extracted from the GraphQL context: diff --git a/src/GraphQL.EntityFramework/EfGraphQLConventions.cs b/src/GraphQL.EntityFramework/EfGraphQLConventions.cs index 215d2e0b9..9e3bf0434 100644 --- a/src/GraphQL.EntityFramework/EfGraphQLConventions.cs +++ b/src/GraphQL.EntityFramework/EfGraphQLConventions.cs @@ -28,10 +28,8 @@ public static void RegisterInContainer( RegisterScalarsAndArgs(services); services.AddHttpContextAccessor(); services.AddTransient(); - services.AddSingleton( - provider => Build(resolveDbContext, model, resolveFilters, provider, disableTracking, disableAsync)); - services.AddSingleton>( - provider => provider.GetRequiredService>()); + services.AddSingleton(provider => Build(resolveDbContext, model, resolveFilters, provider, disableTracking, disableAsync)); + services.AddSingleton>(provider => provider.GetRequiredService>()); } static EfGraphQLService Build( @@ -45,7 +43,7 @@ static EfGraphQLService Build( { model ??= ResolveModel(provider); filters ??= provider.GetService>(); - dbContextResolver ??= _ => DbContextFromProvider(provider); + dbContextResolver ??= (_, requestServices) => DbContextFromProvider(provider, requestServices); return new( model, @@ -55,9 +53,16 @@ static EfGraphQLService Build( disableAsync); } - static TDbContext DbContextFromProvider(IServiceProvider provider) + static TDbContext DbContextFromProvider(IServiceProvider provider, IServiceProvider? requestServices) where TDbContext : DbContext { + var dataFromRequestServices = requestServices? + .GetService(); + if (dataFromRequestServices is not null) + { + return dataFromRequestServices; + } + var dataFromHttpContext = provider.GetService()? .HttpContextAccessor .HttpContext? diff --git a/src/GraphQL.EntityFramework/GraphApi/EfGraphQLService.cs b/src/GraphQL.EntityFramework/GraphApi/EfGraphQLService.cs index 82927cffe..60e8bae44 100644 --- a/src/GraphQL.EntityFramework/GraphApi/EfGraphQLService.cs +++ b/src/GraphQL.EntityFramework/GraphApi/EfGraphQLService.cs @@ -65,8 +65,13 @@ ResolveEfFieldContext BuildContext( User = context.User }; - public TDbContext ResolveDbContext(IResolveFieldContext context) => - resolveDbContext(context.UserContext); + public TDbContext ResolveDbContext(IResolveFieldContext fieldContext) + { + var userContext = fieldContext.UserContext; + var executionContext = fieldContext.ExecutionContext; + var requestServices = executionContext.RequestServices ?? executionContext.ExecutionOptions.RequestServices; + return resolveDbContext(userContext, requestServices); + } Filters? ResolveFilter(IResolveFieldContext context) => resolveFilters?.Invoke(context.UserContext); diff --git a/src/GraphQL.EntityFramework/GraphApi/ResolveDbContext.cs b/src/GraphQL.EntityFramework/GraphApi/ResolveDbContext.cs index 404dba6bb..5b145700e 100644 --- a/src/GraphQL.EntityFramework/GraphApi/ResolveDbContext.cs +++ b/src/GraphQL.EntityFramework/GraphApi/ResolveDbContext.cs @@ -1,4 +1,4 @@ namespace GraphQL.EntityFramework; -public delegate TDbContext ResolveDbContext(object userContext) +public delegate TDbContext ResolveDbContext(object userContext, IServiceProvider? requestServices) where TDbContext : DbContext; \ No newline at end of file diff --git a/src/SampleWeb/Subscription.cs b/src/SampleWeb/Subscription.cs index 8daa86fde..847e3caae 100644 --- a/src/SampleWeb/Subscription.cs +++ b/src/SampleWeb/Subscription.cs @@ -101,7 +101,6 @@ // { // Document = document, // Schema = schema, -// UserContext = new UserContext(dbContext), // Variables = variableValues, // Fragments = document.Fragments, // CancellationToken = token, @@ -141,7 +140,6 @@ // Document = context.Document, // Fragments = context.Fragments, // RootValue = context.RootValue, -// UserContext = context.UserContext, // Operation = context.Operation, // Variables = context.Variables, // CancellationToken = context.CancellationToken, diff --git a/src/SampleWeb/UserContext.cs b/src/SampleWeb/UserContext.cs deleted file mode 100644 index 8f43624d7..000000000 --- a/src/SampleWeb/UserContext.cs +++ /dev/null @@ -1,5 +0,0 @@ -public class UserContext(SampleDbContext context) : - Dictionary -{ - public readonly SampleDbContext DbContext = context; -} \ No newline at end of file diff --git a/src/Tests/DependencyResolutionTests/DependencyTests.cs b/src/Tests/DependencyResolutionTests/DependencyTests.cs index b2795badc..0d714e40c 100644 --- a/src/Tests/DependencyResolutionTests/DependencyTests.cs +++ b/src/Tests/DependencyResolutionTests/DependencyTests.cs @@ -24,7 +24,7 @@ public async Task ExplicitModel() services.AddSingleton(); EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContextSingleDb) userContext).DbContext, + (_, _) => dbContext, sqlInstance.Model); await using var provider = services.BuildServiceProvider(); using var schema = provider.GetRequiredService(); @@ -32,8 +32,8 @@ public async Task ExplicitModel() { Schema = schema, Query = query, - UserContext = new UserContextSingleDb(dbContext), - Variables = null + Variables = null, + RequestServices = provider }; await ExecutionResultData(executionOptions); @@ -50,15 +50,15 @@ public async Task ScopedDbContext() EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContextSingleDb) userContext).DbContext); + (_, requestServices) => requestServices!.GetRequiredService()); await using var provider = services.BuildServiceProvider(); using var schema = new DependencySchema(provider); var executionOptions = new ExecutionOptions { Schema = schema, Query = query, - UserContext = new UserContextSingleDb(dbContext), - Variables = null + Variables = null, + RequestServices = provider }; await ExecutionResultData(executionOptions); @@ -75,15 +75,15 @@ public async Task TransientDbContext() EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContextSingleDb) userContext).DbContext); + (_, requestServices) => requestServices!.GetRequiredService()); await using var provider = services.BuildServiceProvider(); using var schema = new DependencySchema(provider); var options = new ExecutionOptions { Schema = schema, Query = query, - UserContext = new UserContextSingleDb(dbContext), - Variables = null + Variables = null, + RequestServices = provider }; await ExecutionResultData(options); @@ -100,15 +100,15 @@ public async Task SingletonDbContext() EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContextSingleDb) userContext).DbContext); + (_, requestServices) => requestServices!.GetRequiredService()); await using var provider = services.BuildServiceProvider(); using var schema = new DependencySchema(provider); var options = new ExecutionOptions { Schema = schema, Query = query, - UserContext = new UserContextSingleDb(dbContext), - Variables = null + Variables = null, + RequestServices = provider }; await ExecutionResultData(options); diff --git a/src/Tests/IntegrationTests/IntegrationTests.cs b/src/Tests/IntegrationTests/IntegrationTests.cs index 99e755dd8..ff0df5f25 100644 --- a/src/Tests/IntegrationTests/IntegrationTests.cs +++ b/src/Tests/IntegrationTests/IntegrationTests.cs @@ -40,7 +40,7 @@ public async Task SchemaPrint() services.AddSingleton(type); } - EfGraphQLConventions.RegisterInContainer(services, _ => dbContext, dbContext.Model); + EfGraphQLConventions.RegisterInContainer(services, (_, _) => dbContext, dbContext.Model); await using var provider = services.BuildServiceProvider(); using var schema = new Schema(provider); diff --git a/src/Tests/IntegrationTests/QueryExecutor.cs b/src/Tests/IntegrationTests/QueryExecutor.cs index 6fbcc782b..b79244ca7 100644 --- a/src/Tests/IntegrationTests/QueryExecutor.cs +++ b/src/Tests/IntegrationTests/QueryExecutor.cs @@ -12,7 +12,7 @@ public static async Task ExecuteQuery( { EfGraphQLConventions.RegisterInContainer( services, - _ => data, + (_, _) => data, data.Model, _ => filters, disableTracking, @@ -26,8 +26,8 @@ public static async Task ExecuteQuery( Schema = schema, Query = query, ThrowOnUnhandledException = true, - UserContext = new UserContextSingleDb(data), Variables = inputs, + RequestServices = provider, }; var result = await executer.ExecuteWithErrorCheck(options); diff --git a/src/Tests/Mapping/MappingTests.cs b/src/Tests/Mapping/MappingTests.cs index d4ced6989..c8f5762bc 100644 --- a/src/Tests/Mapping/MappingTests.cs +++ b/src/Tests/Mapping/MappingTests.cs @@ -1,4 +1,6 @@ -public class MappingTests +using ExecutionContext = GraphQL.Execution.ExecutionContext; + +public class MappingTests { static SqlInstance sqlInstance; @@ -43,14 +45,21 @@ public async Task Resolve() await database.AddDataUntracked(child, parent); var services = new ServiceCollection(); services.AddSingleton(); - EfGraphQLConventions.RegisterInContainer(services,_ => database.NewDbContext(), model:sqlInstance.Model); + EfGraphQLConventions.RegisterInContainer(services, (_, _) => database.NewDbContext(), model: sqlInstance.Model); await using var provider = services.BuildServiceProvider(); var mappingQuery = provider.GetRequiredService(); + var fieldContext = new ResolveFieldContext + { + ExecutionContext = new ExecutionContext + { + RequestServices = provider + } + }; var resolve = await mappingQuery.Fields .Single(_ => _.Name == "children") .Resolver! - .ResolveAsync(new ResolveFieldContext()); + .ResolveAsync(fieldContext); await Verify(resolve); } @@ -58,7 +67,10 @@ public async Task Resolve() public async Task PropertyToObject() { var expression = Mapper.PropertyToObject("Property"); - var result = expression.Compile()(new() {Property = "value"}); + var result = expression.Compile()(new() + { + Property = "value" + }); await Verify( new { diff --git a/src/Tests/MultiContextTests/MultiContextQuery.cs b/src/Tests/MultiContextTests/MultiContextQuery.cs index 34c0724fa..b5033994a 100644 --- a/src/Tests/MultiContextTests/MultiContextQuery.cs +++ b/src/Tests/MultiContextTests/MultiContextQuery.cs @@ -8,34 +8,18 @@ public MultiContextQuery( efGraphQlService1.AddSingleField( graph: this, name: "entity1", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext1.Entities; - }); + resolve: _ => _.DbContext.Entities); efGraphQlService1.AddFirstField( graph: this, name: "entity1First", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext1.Entities; - }); + resolve: _ => _.DbContext.Entities); efGraphQlService2.AddSingleField( graph: this, name: "entity2", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext2.Entities; - }); + resolve: _ => _.DbContext.Entities); efGraphQlService2.AddFirstField( graph: this, name: "entity2First", - resolve: context => - { - var userContext = (UserContext) context.UserContext; - return userContext.DbContext2.Entities; - }); + resolve: _ => _.DbContext.Entities); } } \ No newline at end of file diff --git a/src/Tests/MultiContextTests/MultiContextTests.cs b/src/Tests/MultiContextTests/MultiContextTests.cs index aeb91010f..5beae514c 100644 --- a/src/Tests/MultiContextTests/MultiContextTests.cs +++ b/src/Tests/MultiContextTests/MultiContextTests.cs @@ -50,10 +50,10 @@ public async Task Run() EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContext) userContext).DbContext1); + (_, requestServices) => requestServices!.GetRequiredService()); EfGraphQLConventions.RegisterInContainer( services, - userContext => ((UserContext) userContext).DbContext2); + (_, requestServices) => requestServices!.GetRequiredService()); #endregion @@ -67,7 +67,7 @@ public async Task Run() { Schema = schema, Query = query, - UserContext = new UserContext(dbContext1, dbContext2) + RequestServices = provider, }; #endregion @@ -75,12 +75,4 @@ public async Task Run() var result = await documentExecuter.ExecuteWithErrorCheck(executionOptions); await Verify(result.Serialize()); } -} - -#region MultiUserContext -public class UserContext(DbContext1 context1, DbContext2 context2) : Dictionary -{ - public readonly DbContext1 DbContext1 = context1; - public readonly DbContext2 DbContext2 = context2; -} -#endregion \ No newline at end of file +} \ No newline at end of file diff --git a/src/Tests/UserContextSingleDb.cs b/src/Tests/UserContextSingleDb.cs deleted file mode 100644 index 8a3a4be0b..000000000 --- a/src/Tests/UserContextSingleDb.cs +++ /dev/null @@ -1,6 +0,0 @@ -public class UserContextSingleDb(TDbContext context) : - Dictionary - where TDbContext : DbContext -{ - public readonly TDbContext DbContext = context; -} \ No newline at end of file