Search code examples
c#asp.net-corehotchocolate

Why is my context disposed before I can use it?


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?


Solution

  • 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";
        }
    }
    

    References