Skip to content
Merged
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
142 changes: 99 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,65 @@

## 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.

## Type-First vs Code-First in GraphQL.NET
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<TodoSchema>()
.AddDI()
.AddGraphTypes()
.AddClrTypeMappings()
.AddExecutionStrategy<SerialExecutionStrategy>(OperationType.Query)
);
```
4. **Create your DI-based graph types**
```csharp
public class TodoMutation : DIObjectGraphBase
{
private readonly IRepository<Todo> _repository;

public TodoMutation(IRepository<Todo> repository)
{
_repository = repository;
}

public async Task<Todo> AddAsync(string title, string? notes)
{
var todo = new Todo {
Title = title,
Notes = notes,
};
return await _repository.InsertAsync(todo, RequestAborted);
}
}
```
5. **Add the new graph types to your schema**
```csharp
public class MutationType : ObjectGraphType
{
public MutationType()
{
Field<NonNullGraphType<TodoMutation>>("todo")
.Resolve(_ => "");
}
}
```

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.

## Background: Type-First vs Code-First in GraphQL.NET

### Type-First Approach

Expand Down Expand Up @@ -58,7 +114,7 @@ public class TodoType : ObjectGraphType<Todo>
```

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.

Expand All @@ -76,8 +132,8 @@ public class TodoType : ObjectGraphType<Todo>
.Resolve(context =>
{
var repository = context.RequestServices!.GetRequiredService<IRepository>();
return repository.GetPersonById(context.Source.CompletedByPersonId));
}
return repository.GetPersonById(context.Source.CompletedByPersonId);
});
}
}
```
Expand Down Expand Up @@ -240,14 +296,14 @@ public static User GetUser(int id) => this.GetById<User>(id);
public static User GetById<T>(this IResolveFieldContext context, int id)
{
var repository = context.RequestServices!.GetRequiredService<IRepository<T>>();
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
Expand Down Expand Up @@ -360,41 +416,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<TodoSchema>()
.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<SerialExecutionStrategy>(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<Query>
DIObjectGraphType<Mutation> 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<TodoSchema>()
.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<SerialExecutionStrategy>(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<Query>
DIObjectGraphType<Mutation> mutationType) // sample where Mutation inherits DIObjectGraphBase
: base(serviceProvider)
{
Query = queryType;
Mutation = mutationType;
}
}
```

## Additional Samples

Expand Down
Loading