Code samples used in this blog series have been updated to latest version of .NET Core (5.0.4) and GraphQL-Dotnet (4.2.0). Follow this link to get the updated samples.
With the updated version (4.2.0) of GraphQL-Dotnet, the SubscriptionExecutionStrategy
feature is no longer a part of the core library. It's shipped as a stand-alone NuGet package,
Install-Package GraphQL.SystemReactive -Version 4.2.0
Faking with an in-memory database is simple and easy but sooner or later you are going to need a real database. A typical SQL server database setup is just a connection string away. Modify the EF Core DBContext
service registration and pass a connection string of a SQL Server instance as an option,
The logger is configured to log the result of various sql queries generated within the application life-cycle.
We do have a problem with our application. And that is,
Entity framework can't execute queries in parallel. So we use async/await to finish running one operation before starting another.
Execution Strategies
By default, graphql-dotnet
executes queries in parallel. Most of the time that's the desired behavior. But there is a catch when a parent graph type has dependencies on a child graph type which also uses asynchronous tasks to resolve field values. So even if the parent task is awaited in ExecuteNodeAsync
, the child task will not await this call all the time.
And we will sometimes get exceptions like,
System.InvalidOperationException: A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext.
Let's take the Orders
query for example. Along with all the orders, if we intend to get the customer for those orders, we will query for something like,
query GetOrders {
orders {
tag
createdAt
customer {
name
billingAddress
}
}
}
The application may or may not (most of the time will) throw this following exception,
Notice that because of parallel execution of the queries, the customer
field for the second order
evaluates to null
.
We can change this execution behavior by implementing our document executer and replace it with the default DocumentExecuter
. The following snippet does that for you,
Notice, instead of ParallelExecutionStrategy
, we intend to go for SerialExecutionStrategy
for every query and mutation.
All done except for registering this implementation of DocumentExecuter
in the IOC,
Comments