I'm implementing a very simple query using the HotChocolate
GraphQL library in a .Net 5 project. I've been following the tutorial series from the HotChocolate GitHub repository but in my project I don't want GraphQL accessing the context directly and would instead prefer for it to access a repository that manages database access via the context.
I've built a few REST endpoints in this project alongside GraphQL and this repository pattern works properly there. However when I call these repository methods from a GraphQL, the context is disposed before the repository method uses it.
I am guessing that the way HotChocolate uses the context causes it to get disposed earlier than I am expecting, but I am having trouble figuring out when/where it gets disposed and how I can prevent it from being disposed so my repository methods will work.
ContentRepository.cs
namespace BLL.Repository
{
public class ContentRepository : IRepository<Content>
{
private readonly CmsContext _dbContext;
public ContentRepository(CmsContext dbContext)
{
_dbContext = dbContext;
}
public virtual List<Content> GetContent()
{
return _dbContext.Content.ToList();
}
}
}
Query.cs
namespace Web.GraphQL
{
public class Query
{
private readonly IContentRepository _contentRepository;
public Query(IContentRepository contentRepository)
{
_contentRepository = contentRepository;
}
public List<Content> GetContent()
{
return _contentRepository.GetContent() as List<Content>;
}
}
}
Startup.cs
namespace Web
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddControllers().AddNewtonsoftJson();
services.AddDbContext<CmsContext>(options =>
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), ServerVersion.AutoDetect(Configuration.GetConnectionString("DefaultConnection"))));
services
.AddGraphQLServer()
.ModifyRequestOptions(options => options.IncludeExceptionDetails = true)
.AddQueryType<Query>();
services.AddScoped<IRepository<Content>, ContentRepository>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "/{controller}/{action}",
new {controller = "Default", action = "Index"});
});
app.UseEndpoints(endpoints => endpoints.MapGraphQL());
app.UseGraphQLVoyager(new VoyagerOptions
{
GraphQLEndPoint = "/graphql"
}, "/graphql-voyager");
}
}
}
When debugging and stepping through the code, once it has reached the GetContent
method in ContentRepository.cs
it throws an ObjectDisposedException
.
What do I need to do to ensure that CmsContext
is still available to the ContentRepository
when called from a GraphQL query?
You can only use constructor injection with HotChocolate v11:
services
.AddScoped<IUserRepository, UserRepository>()
.AddScoped<Query>()
.AddGraphQLServer()
.AddQueryType<Query>()
public class Query
{
public Query(IUserRepository repository)
{
}
// code omitted for brevity
}
If you're on HotChocolate v10, you need to use parameter injection with [Service]
attribute:
public class Query
{
public string Bar(ISchema schema, [Service]MyCustomService service)
{
return "foo";
}
}