graphql-platform
graphql-platform copied to clipboard
Using the DbContext through constructor injection in the service layer results in errors
Is your feature request related to a problem?
So in our project we want to design our application in a way that we can easily add different API interfaces like GraphQL or a RestAPI on our existing business logic.
To achieve this we moved as much logic as possible to regular Services which live in the DI container. This services are made using regular microsoft standards, like injecting the DbContext normally through the constructor.
Now we do have a small problem, for Queries we want the service to pass back a IQueryable of the DbSet in the DbContext and pass this to the Query class so we can use the projection option and only retrieve the fields that the query requests.
Problem is that when injecting the DbContext in the service the regular way it will start throwing an Cannot access a disposed context instance error.
I have an example project here: https://github.com/bramvelderen-nlo/Playday.GraphQL
- The service: https://github.com/bramvelderen-nlo/Playday.GraphQL/blob/main/src/Playday.GraphQL.API/Graph/Scooters/ScooterService.cs
- The query class: https://github.com/bramvelderen-nlo/Playday.GraphQL/blob/main/src/Playday.GraphQL.API/Graph/Scooters/ScooterQueries.cs
- The program class: https://github.com/bramvelderen-nlo/Playday.GraphQL/blob/main/src/Playday.GraphQL.API/Program.cs
The solution you'd like
What i hope is possible is that i can just build my service layer the same way as i would for an MVC project and reuse the exact logic for the GraphQL resolvers. But right now that gives me the DbContext error. So is there already an existing solution here where i don't need to pass the DbContext from the Query class to the service and instead can just rely on the injected DbContext?
Product
Hot Chocolate
I think this is happening, since the service scope is disposed after the resolver itself finishes executing: https://github.com/ChilliCream/hotchocolate/blob/80350d82253419a9ff631bbbaae0f640eb834753/src/HotChocolate/Core/src/Types/Types/Extensions/ObjectFieldDescriptorExtensions.cs#L136-L158
Seems like a similar problem to https://github.com/ChilliCream/hotchocolate/issues/4638 for me.
If you do not depend on the UseServiceScope, you could simply register your service using the ServiceKind.Synchronized: https://chillicream.com/docs/hotchocolate/server/dependency-injection/#registerservice
@tobias-tengler I've seen the Synchronized option indeed. But doesn't that mean that the resolvers will all run synchronous instead of parallel. While using the UseServiceScope will still run paralel but each of the injected services still have their own scoped dbcontext.
But shouldn't it be the case that the Projections of HotChocolate should also run in the same ServiceScope as all the services injected within? Or is this somehow possible with settings or attributes?
I just looked at your code again and saw that you don't have the UseProjection attribute there atm, so I can't tell how you've used it. Did you put the UseServiceScope attribute on top of the UseProjection attribute? Normally the services should only be disposed once the inner middleware has finished.
@tobias-tengler I've seen the Synchronized option indeed. But doesn't that mean that the resolvers will all run synchronous instead of parallel. While using the UseServiceScope will still run paralel but each of the injected services still have their own scoped dbcontext.
Have you tested the performance of it? Normally there are only a few fields in a query that will use the DbContext. The performance impact should be negligable most of the time.
Did you put the UseServiceScope attribute on top of the UseProjection attribute? Normally the services should only be disposed once the inner middleware has finished. That is what i was hoping for. But as soon as i return a IQueryable i get this issue that the DbContext is already disposed, so apparently in the HotChocolate middleware which executes the IQueryable runs outside of the ServiceScope, causing this error
Right now i indeed don't have the UseProjection anywere since i didn't need it to reproduce the issue as seen in the example. But ideally i have exactly this example code with the useProjection also yes.
Have you tested the performance of it? Normally there are only a few fields in a query that will use the DbContext. The performance impact should be negligable most of the time.
I did not test the performance impact, it will be negligable indeed, so i could go for that option. But i feel like the other option should somehow work too.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@tobias-tengler I've seen the Synchronized option indeed. But doesn't that mean that the resolvers will all run synchronous instead of parallel. While using the UseServiceScope will still run paralel but each of the injected services still have their own scoped dbcontext.
But shouldn't it be the case that the Projections of HotChocolate should also run in the same ServiceScope as all the services injected within? Or is this somehow possible with settings or attributes?
You can register your context as scoped while using the pooled db context factory. So the db context has the same lifetime as the scope and the resolver since you're using UseServiceScope.
https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-di%2Cexpression-api-with-constant#managing-state-in-pooled-contexts