UseInternalServiceProvider breaks Thinktecture.EntityFrameworkCore temp tables
When using DbContextOptionsBuilder.UseInternalServiceProvider() with Thinktecture.EntityFrameworkCore temp tables feature, multiple services registered by SqlServerDbContextOptionsExtension are not available in EF Core's internal service provider, causing temp table operations to fail completely.
Some background info:
With UseInternalServiceProvider() EF Core infrastructure bypasses the extensions and expects everything to be registered with the service provider directly. EF Core providers offer "special" extension methods like myServiceCollection.AddEntityFrameworkSqlServer(). I don't provide any "special" methods because I don't recommend our customers using UseInternalServiceProvider().
The only workaround is to use extensions directly, and not the helper methods on SqlServerDbContextOptionsExtensions.
For example, having following db context configuration:
var services = new ServiceCollection()
.AddEntityFrameworkSqlServer()
.AddDbContext<TestDbContext>((sp, b) =>
{
b.UseSqlServer("conn-string",
sqlBuilder => sqlBuilder.AddBulkOperationSupport(
addBulkOperationSupport: true,
configureTempTablesForPrimitiveTypes: true))
.UseInternalServiceProvider(sp);
});
instead of using
sqlBuilder.AddBulkOperationSupport(
addBulkOperationSupport: true,
configureTempTablesForPrimitiveTypes: true))
use
var relationalExtensions = new RelationalDbContextOptionsExtension();
var sqlServerExtensions = new SqlServerDbContextOptionsExtension(relationalExtensions)
{
AddBulkOperationSupport = true,
ConfigureTempTablesForPrimitiveTypes = true
};
relationalExtensions.ApplyServices(services);
sqlServerExtensions.ApplyServices(services);
The full configuration looks like as following:
var services = new ServiceCollection()
// Create and configure the extensions.
var relationalExtensions = new RelationalDbContextOptionsExtension();
var sqlServerExtensions = new SqlServerDbContextOptionsExtension(relationalExtensions)
{
AddBulkOperationSupport = true,
ConfigureTempTablesForPrimitiveTypes = true
};
// Add dependencies to service collection.
relationalExtensions.ApplyServices(services);
sqlServerExtensions.ApplyServices(services);
services
.AddEntityFrameworkSqlServer()
.AddDbContext<TestDbContext>((serviceProvider, builder) =>
{
builder.UseSqlServer("conn-string")
.UseInternalServiceProvider(serviceProvider);
// Some services need the extensions to be registered in the options builder.
var extensionsInfra = (IDbContextOptionsBuilderInfrastructure)builder;
extensionsInfra.AddOrUpdateExtension(relationalExtensions);
extensionsInfra.AddOrUpdateExtension(sqlServerExtensions);
});
Although I made a few tests and it works, still, there is no guarantee that you won't run into other issues.
Thank you for your answer, and effort providing the background info. Do you think it's worth adding an extension method AddBulkOperationSupportWithInternalServiceProvider ?
With the current state of the code, 1 extension won't be enough. We need 1 method for registration of the dependencies with DI, and another one for registration of extensions with DbContextOptions. The code should be refactored so we don't need the second one. But the biggest challenge will be the testing of all features with internal service provider.