From a433fbd7c338070b0ce5486abad815f203c8c1b6 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Sat, 1 Mar 2025 17:23:05 -0500 Subject: [PATCH 1/3] Add Getting Started section to readme --- README.md | 116 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 6ae2d8f..80d5434 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,38 @@ GraphQL.DI enhances GraphQL.NET's code-first approach by providing dependency injection support for field resolvers through the `DIObjectGraphBase` class. This enables a more maintainable and testable approach to building GraphQL APIs by allowing services to be injected directly into your field resolver classes. +## Getting Started + +1. **Install the NuGet package** + ```bash + dotnet add package Shane32.GraphQL.DI + ``` +2. **Add `.AddDI()` to your `AddGraphQL(...)` registration** + Register your schema and resolvers, for example: + ```csharp + services.AddGraphQL(b => b + .AddSystemTextJson() + .AddSchema() + .AddDI() + .AddGraphTypes() + .AddClrTypeMappings() + .AddExecutionStrategy(OperationType.Query) + ); + ``` +3. **Create and use your DI-based resolvers** + ```csharp + public class TodoResolver : DIObjectGraphBase + { + private readonly IRepository _repository; + public TodoResolver(IRepository repository) => _repository = repository; + + public async Task CompletedBy() + => await _repository.GetPersonById(Source.CompletedByPersonId); + } + ``` + +See the [Setup](#setup) section below for more detailed instructions on configuring GraphQL.DI in your project. + ## Type-First vs Code-First in GraphQL.NET ### Type-First Approach @@ -37,8 +69,8 @@ public class Todo While services can be injected in the pattern shown above, there are two issues with this approach: -1. Injection of services is not within the constructor and can be considered an antipattern -2. Resolver code is mixed together with your data model +1. Injection of services is not within the constructor and can be considered an antipattern +2. Resolver code is mixed together with your data model ### Code-First Approach @@ -58,7 +90,7 @@ public class TodoType : ObjectGraphType ``` This can solve both issues noted above -- the data model is separate from the GraphQL type definition, -and services can be resolved via dependency injection in the constructor. However, graph types are +and services can be resolved via dependency injection in the constructor. However, graph types are effectively singletons (typically) within the dependency injection container, so if your services (such as `IRepository` above) is a scoped service, then your code will not run properly. @@ -76,8 +108,8 @@ public class TodoType : ObjectGraphType .Resolve(context => { var repository = context.RequestServices!.GetRequiredService(); - return repository.GetPersonById(context.Source.CompletedByPersonId)); - } + return repository.GetPersonById(context.Source.CompletedByPersonId); + }); } } ``` @@ -240,14 +272,14 @@ public static User GetUser(int id) => this.GetById(id); public static User GetById(this IResolveFieldContext context, int id) { var repository = context.RequestServices!.GetRequiredService>(); - return repository.GetById(int); + return repository.GetById(id); } ``` ## Advanced Usage Please note that unlike GraphQL.NET type-first resolvers, only public methods are resolved by default. -Properties and field are ignored, as well as private or protected members. +Properties and fields are ignored, as well as private or protected members. This more closely mimics the design of controllers within ASP.NET. ### Service Lifetime @@ -360,41 +392,41 @@ All other type-first attributes from GraphQL.NET are supported, such as `[Id]`, ## Setup -1. Install the NuGet package: - -```bash -dotnet add package Shane32.GraphQL.DI -``` - -2. Register your types with the DI container: - -```csharp -services.AddGraphQL(b => b - .AddSystemTextJson() - .AddSchema() - .AddDI() // Register and configure GraphQL.DI types defined within the assembly - .AddGraphTypes() // Register GraphQL.NET types defined within the assembly - .AddClrTypeMappings() // Enable automatic CLR type mappings - .AddExecutionStrategy(OperationType.Query) // Specify serial execution strategy -); -``` - -3. Define your schema with root DI graph types (if/as needed): - -```csharp -public class TodoSchema : Schema -{ - public TodoSchema( - IServiceProvider serviceProvider, - QueryType queryType, // sample where QueryType inherits DIObjectGraphType - DIObjectGraphType mutationType) // sample where Mutation inherits DIObjectGraphBase - : base(serviceProvider) - { - Query = queryType; - Mutation = mutationType; - } -} -``` +1. **Install the NuGet package:** + + ```bash + dotnet add package Shane32.GraphQL.DI + ``` + +2. **Register your types with the DI container:** + + ```csharp + services.AddGraphQL(b => b + .AddSystemTextJson() + .AddSchema() + .AddDI() // Register and configure GraphQL.DI types defined within the assembly + .AddGraphTypes() // Register GraphQL.NET types defined within the assembly + .AddClrTypeMappings() // Enable automatic CLR type mappings + .AddExecutionStrategy(OperationType.Query) // Specify serial execution strategy + ); + ``` + +3. **Define your schema with root DI graph types (if/as needed):** + + ```csharp + public class TodoSchema : Schema + { + public TodoSchema( + IServiceProvider serviceProvider, + QueryType queryType, // sample where QueryType inherits DIObjectGraphType + DIObjectGraphType mutationType) // sample where Mutation inherits DIObjectGraphBase + : base(serviceProvider) + { + Query = queryType; + Mutation = mutationType; + } + } + ``` ## Additional Samples From 8ee15879352d796f616202491cc641ab9115024f Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Sat, 1 Mar 2025 17:33:08 -0500 Subject: [PATCH 2/3] Update README.md --- README.md | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 80d5434..89f8b86 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ ## Overview -GraphQL.DI enhances GraphQL.NET's code-first approach by providing dependency injection support for field resolvers through the `DIObjectGraphBase` class. This enables a more maintainable and testable approach to building GraphQL APIs by allowing services to be injected directly into your field resolver classes. +GraphQL.DI enhances GraphQL.NET's code-first approach by providing dependency injection support for +field resolvers through the `DIObjectGraphBase` class. This enables a more maintainable and testable +approach to building GraphQL APIs by allowing services to be injected directly into your field resolver classes. ## Getting Started @@ -24,21 +26,43 @@ GraphQL.DI enhances GraphQL.NET's code-first approach by providing dependency in .AddExecutionStrategy(OperationType.Query) ); ``` -3. **Create and use your DI-based resolvers** +4. **Create your DI-based graph types** ```csharp - public class TodoResolver : DIObjectGraphBase + public class TodoMutation : DIObjectGraphBase { - private readonly IRepository _repository; - public TodoResolver(IRepository repository) => _repository = repository; - - public async Task CompletedBy() - => await _repository.GetPersonById(Source.CompletedByPersonId); + private readonly IRepository _repository; + + public TodoMutation(IRepository repository) + { + _repository = repository; + } + + public async Task AddAsync(string title, string notes) + { + var todo = new Todo { + Title = title, + Notes = notes, + }; + return await _repository.AddAsync(todo, RequestAborted); + } + } + ``` +5. **Add the new graph types to your schema** + ```csharp + public class MutationType : ObjectGraphType + { + public MutationType() + { + Field>("todo") + .Resolve(_ => ""); + } } ``` -See the [Setup](#setup) section below for more detailed instructions on configuring GraphQL.DI in your project. +See the [Setup](#setup) section below for more detailed instructions on configuring GraphQL.DI in +your project, including how to configure root types for use with GraphQL.DI. -## Type-First vs Code-First in GraphQL.NET +## Background: Type-First vs Code-First in GraphQL.NET ### Type-First Approach @@ -69,8 +93,8 @@ public class Todo While services can be injected in the pattern shown above, there are two issues with this approach: -1. Injection of services is not within the constructor and can be considered an antipattern -2. Resolver code is mixed together with your data model +1. Injection of services is not within the constructor and can be considered an antipattern +2. Resolver code is mixed together with your data model ### Code-First Approach From 218b854744607367041b426f98b18c5f6755a41b Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Sat, 1 Mar 2025 17:35:40 -0500 Subject: [PATCH 3/3] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 89f8b86..d6c55bc 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,13 @@ approach to building GraphQL APIs by allowing services to be injected directly i _repository = repository; } - public async Task AddAsync(string title, string notes) + public async Task AddAsync(string title, string? notes) { var todo = new Todo { Title = title, Notes = notes, }; - return await _repository.AddAsync(todo, RequestAborted); + return await _repository.InsertAsync(todo, RequestAborted); } } ```