Skip to content

Commit

Permalink
Fixes to implicit DbContext registration around factories
Browse files Browse the repository at this point in the history
Fixes #35484
Fixes #35485
  • Loading branch information
roji committed Jan 16, 2025
1 parent be2ca60 commit 296128f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ public static IServiceCollection AddDbContextFactory
serviceCollection.TryAdd(
new ServiceDescriptor(
typeof(TContext),
typeof(TContext),
sp => sp.GetRequiredService<IDbContextFactory<TContext>>().CreateDbContext(),
lifetime == ServiceLifetime.Transient
? ServiceLifetime.Transient
: ServiceLifetime.Scoped));
Expand Down Expand Up @@ -1024,6 +1024,7 @@ public static IServiceCollection AddPooledDbContextFactory
serviceCollection.TryAddSingleton<IDbContextPool<TContext>, DbContextPool<TContext>>();
serviceCollection.TryAddSingleton<IDbContextFactory<TContext>>(
sp => new PooledDbContextFactory<TContext>(sp.GetRequiredService<IDbContextPool<TContext>>()));
serviceCollection.TryAddScoped<TContext>(sp => sp.GetRequiredService<IDbContextFactory<TContext>>().CreateDbContext());

return serviceCollection;
}
Expand Down
78 changes: 76 additions & 2 deletions test/EFCore.Tests/DbContextFactoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,42 @@ private static void ContextFactoryTest<TContext>(ServiceLifetime lifetime)
Assert.Throws<ObjectDisposedException>(() => context2.Model);
}

[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
public void Registering_factory_also_registers_a_scoped_context(bool pooled)
{
using var serviceProvider = RegisterContextFactory<WoolacombeContext>(new ServiceCollection(), pooled).BuildServiceProvider();

WoolacombeContext context1;

using (var scope1 = serviceProvider.CreateScope())
{
context1 = scope1.ServiceProvider.GetRequiredService<WoolacombeContext>();
var context2 = scope1.ServiceProvider.GetRequiredService<WoolacombeContext>();

Assert.Same(context1, context2); // Assert not transient
}

Assert.Throws<ObjectDisposedException>(() => context1.Model); // Assert not singleton
}

[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
public async Task Implicitly_registered_context_gets_created_via_user_factory_type(bool pooled)
{
await using var serviceProvider = new ServiceCollection()
.AddDbContextFactory<WoolacombeContext, WoolacombeContextFactory>(
b => b.UseInMemoryDatabase(nameof(WoolacombeContext)))
.BuildServiceProvider();

var factory = (WoolacombeContextFactory)serviceProvider.GetRequiredService<IDbContextFactory<WoolacombeContext>>();
using var scope = serviceProvider.CreateScope();
await using var context = scope.ServiceProvider.GetRequiredService<WoolacombeContext>();
Assert.True(factory.WasCalled);
}

[ConditionalFact]
public void Factory_can_use_pool()
{
Expand Down Expand Up @@ -153,6 +189,26 @@ public void Factory_can_use_shared_pool()
Assert.NotSame(context2b, context3b);
}

[ConditionalFact]
public void Implicitly_registered_context_gets_pooled()
{
var serviceProvider = RegisterContextFactory<WoolacombeContext>(new ServiceCollection(), pooled: true).BuildServiceProvider();

WoolacombeContext context1, context2;

using (var scope = serviceProvider.CreateScope())
{
context1 = scope.ServiceProvider.GetRequiredService<WoolacombeContext>();
}

using (var scope = serviceProvider.CreateScope())
{
context2 = scope.ServiceProvider.GetRequiredService<WoolacombeContext>();
}

Assert.Same(context1, context2);
}

[ConditionalTheory]
[InlineData(ServiceLifetime.Singleton)]
[InlineData(ServiceLifetime.Scoped)]
Expand Down Expand Up @@ -566,10 +622,13 @@ public void Application_can_register_factory_implementation_in_AddDbContextFacto

private class WoolacombeContextFactory(DbContextOptions<WoolacombeContext> options) : IDbContextFactory<WoolacombeContext>
{
private readonly DbContextOptions<WoolacombeContext> _options = options;
public bool WasCalled { get; private set; }

public WoolacombeContext CreateDbContext()
=> new(_options);
{
WasCalled = true;
return new WoolacombeContext(options);
}
}

[ConditionalTheory]
Expand Down Expand Up @@ -851,6 +910,21 @@ private class CustomModelCustomizer(ModelCustomizerDependencies dependencies) :

private class FactoryModelCustomizer(ModelCustomizerDependencies dependencies) : ModelCustomizer(dependencies);

private IServiceCollection RegisterContextFactory<TContext>(IServiceCollection serviceCollection, bool pooled)
where TContext : DbContext
{
if (pooled)
{
serviceCollection.AddPooledDbContextFactory<TContext>(b => b.UseInMemoryDatabase(nameof(TContext)));
}
else
{
serviceCollection.AddDbContextFactory<TContext>(b => b.UseInMemoryDatabase(nameof(TContext)));
}

return serviceCollection;
}

private static string GetStoreName(DbContext context1)
=> context1.GetService<IDbContextOptions>().FindExtension<InMemoryOptionsExtension>().StoreName;
}

0 comments on commit 296128f

Please sign in to comment.