This is documentation for v16, which is currently in preview.
See the latest stable version instead.

Entity Framework Core

Learn how to integrate Entity Framework Core with Hot Chocolate v16, including DbContext injection and factory patterns.

Entity Framework Core is a powerful object-relational mapping framework that has become a staple when working with SQL-based databases in .NET applications.

Resolver Injection of a DbContext

When using the default scope for queries, each resolver that accepts a scoped DbContext receives a separate instance. This avoids threading issues.

C#
public static async Task<Book?> GetBookByIdAsync(
ApplicationDbContext dbContext) => // ...

When using the default scope for mutations, each mutation resolver that accepts a scoped DbContext receives the same request-scoped instance, as mutations execute sequentially.

C#
public static async Task<Book> AddBookAsync(
AddBookInput input,
AppDbContext dbContext) => // ...

See the Dependency Injection documentation for more details.

Warning: Changing the default scope for queries will likely result in the error "A second operation started on this context before a previous operation completed", because Entity Framework Core does not support multiple parallel operations on the same DbContext instance.

Using a DbContext Factory

To use a DbContext factory, register your DbContext with Hot Chocolate. Install the additional package:

Bash
dotnet add package HotChocolate.Data.EntityFramework
Warning
All HotChocolate.* packages need to have the same version.

Call the RegisterDbContextFactory<T> method on the IRequestExecutorBuilder. The Hot Chocolate resolver compiler then takes care of injecting your DbContext instance into resolvers.

C#
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddDbContextFactory<ApplicationDbContext>(
options => options.UseSqlServer("YOUR_CONNECTION_STRING"));
// ... or AddPooledDbContextFactory.
builder
.AddGraphQL()
.RegisterDbContextFactory<ApplicationDbContext>()
.AddTypes();
C#
[QueryType]
public static class Query
{
public static async Task<Book?> GetBookByIdAsync(
Guid id,
ApplicationDbContext dbContext)
{
return await dbContext.Books.FindAsync(id);
}
}

Warning: You still need to add your DbContextFactory to the dependency injection container by calling AddDbContextFactory<T> or AddPooledDbContextFactory<T>. RegisterDbContextFactory<T> on its own is not enough.

Working with a DbContext Factory

When you use a DbContext factory, you need to access the DbContext differently outside of direct resolver injection.

DataLoaders

When creating DataLoaders that need access to your DbContext, inject the IDbContextFactory<T> through the constructor. Create and dispose the DbContext within the LoadBatchAsync method.

C#
public sealed class BookByIdDataLoader : BatchDataLoader<Guid, Book>
{
private readonly IDbContextFactory<AppDbContext>
_dbContextFactory;
public BookByIdDataLoader(
IDbContextFactory<AppDbContext> dbContextFactory,
IBatchScheduler batchScheduler,
DataLoaderOptions options)
: base(batchScheduler, options)
{
_dbContextFactory = dbContextFactory;
}
protected override async Task<IReadOnlyDictionary<Guid, Book>>
LoadBatchAsync(
IReadOnlyList<Guid> keys,
CancellationToken cancellationToken)
{
using AppDbContext dbContext =
_dbContextFactory.CreateDbContext();
return await dbContext.Books
.Where(b => keys.Contains(b.Id))
.ToDictionaryAsync(b => b.Id, cancellationToken);
}
}

Warning: Dispose the DbContext after use. The example above uses the using statement for this purpose.

Services

Services that need a DbContext should inject IDbContextFactory<T> instead of the DbContext directly.

C#
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContextFactory<ApplicationDbContext>(
options => options.UseSqlServer("YOUR_CONNECTION_STRING"));
builder.Services.AddScoped<BookService>();
builder
.AddGraphQL()
.AddTypes();
C#
public sealed class BookService : IAsyncDisposable
{
private readonly ApplicationDbContext _dbContext;
public BookService(
IDbContextFactory<ApplicationDbContext> dbContextFactory)
{
_dbContext = dbContextFactory.CreateDbContext();
}
public async Task<Book?> GetBookAsync(Guid id)
{
return await _dbContext.Books.FindAsync(id);
}
public ValueTask DisposeAsync()
{
return _dbContext.DisposeAsync();
}
}
C#
[QueryType]
public static class Query
{
public static async Task<Book?> GetBookByIdAsync(
Guid id,
BookService bookService)
{
return await bookService.GetBookAsync(id);
}
}

Warning: Dispose the DbContext when the service is disposed. The example above implements IAsyncDisposable and disposes the DbContext in DisposeAsync.

Next Steps

Last updated on April 23, 2026 by Rafael Staib