Search code examples
asp.net-core.net-coreentity-framework-coresnowflake-cloud-data-platformhotchocolate

AddPooledDbContextFactory with DbContext that uses custom DbConnection: service not registered


I have a custom DbContext SnowflakeDbContext that I need to initialize with a SnowflakeDbConnection for it to work:

    public class SnowflakeDbContext : DbContext
    {
        private readonly string connectionString = "";

        public SnowflakeDbContext(DbContextOptions<SnowflakeDbContext> options) : base(options)
        {

        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            var dbConnection = new SnowflakeDbConnection()
            {
                ConnectionString = this.connectionString
            };
            optionsBuilder.UseSqlServer(dbConnection);
            optionsBuilder.AddInterceptors(new SnowflakeCommandInterceptor());
        }

        public DbSet<Opportunity> Opportunities { get; set; } = default!;
        public DbSet<Account> Accounts { get; set; } = default!;
    }

This works well with EF Core 5, were in Startup.cs (I am using an ASP.NET Core 5 web application) I use

      .AddDbContext<SnowflakeDbContext>(ServiceLifetime.Singleton)

I want to use the SnowflakeDbContext with HotChocolate where it is recommended that I use AddPooledDbContextFactory<> in order to support pooling of connections and allowing the system to make simultaneous calls (described here).

I have modified Startup.cs:

        public void ConfigureServices(IServiceCollection services)
        {

            services
                .AddPooledDbContextFactory<SnowflakeDbContext>(options =>
                {
                    var dbConnection = new SnowflakeDbConnection()
                    {
                        ConnectionString = this.connectionString
                    };

                    options.UseSqlServer(dbConnection);
                    options.AddInterceptors(new SnowflakeCommandInterceptor());
                })
                .AddGraphQLServer()
                .AddQueryType<Query>();
        }

Using the following GraphQL query (which uses parallel queries):

query GetAccountsInParallel {
  a: accounts {
    id, name
  }
  b: accounts {
    id, name
  }
  c: accounts {
    id, name
  }
}

I get the following error:

"No service for type 'SnowflakeGraphQL.Snowflake.SnowflakeDbContext' has been registered.",

I can add

      .AddDbContext<SnowflakeDbContext>()

in Startup.cs after the call to .AddPooledDbContextFactory<>. Now I get a different error:

"A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext."

All of the examples I have seen on the web use .UseSqlServer(connectionString) where as I need to use the .UseSqlServer(dbConnection) version in order to be able to access our Snowflake database.

How do I configure my application in Startup.cs to use .AddPooledDbContextFactory()?

Update: Starting with the graphql-workshop code and replacing Sqlite with first SqlServer and then SqlServer using my SnowflakeDbContext I get it to work, so there must be a subtle difference somewhere in my code as described above that results in a failure in my case.


Solution

  • When retrieving the accounts records, we need to use the [ScopedService] rather than the [Service] like this:

       [UseApplicationDbContext]
       public async Task<List<Account>> GetAccounts([ScopedService] SnowflakeDbContext context) => await context.Accounts.ToListAsync();