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,

public static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options => options.UseLoggerFactory(loggerFactory).UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=GameStoreDb;Trusted_Connection=True;"), ServiceLifetime.Transient);
}
Startup.cs

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,

public class SerialDocumentExecuter : DocumentExecuter
{
    private static IExecutionStrategy ParallelExecutionStrategy = new ParallelExecutionStrategy(); 
    private static IExecutionStrategy SerialExecutionStrategy = new SerialExecutionStrategy();
    private static IExecutionStrategy SubscriptionExecutionStrategy = new SubscriptionExecutionStrategy();

    protected override IExecutionStrategy SelectExecutionStrategy(ExecutionContext context)
    {
        return context.Operation.OperationType switch
        {
            OperationType.Query => SerialExecutionStrategy,
            OperationType.Mutation => SerialExecutionStrategy,
            OperationType.Subscription => SubscriptionExecutionStrategy,
            _ => throw new InvalidOperationException($"Unexpected OperationType {context.Operation.OperationType}"),
        };
    }
}
SerialDocumentExecuter.cs

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,

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDocumentExecuter, SerialDocumentExecuter>();
}
Startup.cs

Part-X

fiyazbinhasan/GraphQLCoreFromScratch
https://fiyazhasan.me/tag/graphql/. Contribute to fiyazbinhasan/GraphQLCoreFromScratch development by creating an account on GitHub.
ParallelExecution in queries · Issue #1310 · graphql-dotnet/graphql-dotnet
Problem I am using EFCore + DataLoaders and trying to query { root { nested1 { nomatter } nested2 { nomatter } } } Each nested node using DataLoader with access to DbContext from UserContext from H...