Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,61 @@ public static IServiceCollection AddPooledDbContextFactory
return serviceCollection;
}

/// <summary>
/// Registers a pooled <see cref="IDbContextFactory{TContext}" /> in the
/// <see cref="IServiceCollection" /> for creating instances of the specified
/// <see cref="DbContext" /> type, using a custom pool size.
/// </summary>
/// <remarks>
/// <para>
/// This overload aligns with the EF Core centralized configuration model
/// <see cref="ConfigureDbContext{TContext}(IServiceCollection, Action{DbContextOptionsBuilder}, ServiceLifetime)" />.
/// and allows specifying a custom <paramref name="poolSize"/>.
/// Options configured via <c>ConfigureDbContext&lt;TContext&gt;</c> are automatically used,
/// eliminating the need to repeat provider configuration.
/// </para>
/// <para>
/// Use this method when using dependency injection in your application, such as with ASP.NET Core.
/// For applications that don't use dependency injection, consider creating <see cref="DbContext" />
/// instances directly with its constructor. The <see cref="DbContext.OnConfiguring" /> method can then be
/// overridden to configure a connection string and other options.
/// </para>
/// <para>
/// Entity Framework Core does not support multiple parallel operations being run on the same <see cref="DbContext" />
/// instance. This includes both parallel execution of async queries and any explicit concurrent use from multiple threads.
/// Therefore, always await async calls immediately, or use separate DbContext instances for operations that execute
/// in parallel. See <see href="https://aka.ms/efcore-docs-threading">Avoiding DbContext threading issues</see> for more information
/// and examples.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-di">Using DbContext with dependency injection</see>,
/// <see href="https://aka.ms/efcore-docs-dbcontext-factory">Using DbContext factories</see>, and
/// <see href="https://aka.ms/efcore-docs-dbcontext-pooling">Using DbContext pooling</see>
/// for more information and examples.
/// </para>
/// </remarks>
/// <typeparam name="TContext">
/// The type of <see cref="DbContext" /> to be created by the factory.
/// </typeparam>
/// <param name="serviceCollection">
/// The <see cref="IServiceCollection" /> to which the services are added.
/// </param>
/// <param name="poolSize">
/// The maximum number of <typeparamref name="TContext" /> instances retained by the pool. Defaults to 1024.
/// </param>
/// <returns>
/// The same <see cref="IServiceCollection" /> so that multiple calls can be chained.
/// </returns>
public static IServiceCollection AddPooledDbContextFactory
<[DynamicallyAccessedMembers(DbContext.DynamicallyAccessedMemberTypes)] TContext>(
this IServiceCollection serviceCollection,
int poolSize = DbContextPool<DbContext>.DefaultPoolSize)
where TContext : DbContext
=> AddPooledDbContextFactory<TContext>(
serviceCollection,
static (_, __) => { },
poolSize);

/// <summary>
/// Configures the given context type in the <see cref="IServiceCollection" />.
/// </summary>
Expand Down
38 changes: 38 additions & 0 deletions test/EFCore.SqlServer.FunctionalTests/DbContextPoolingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,43 @@ public void Options_modified_in_on_configuring_with_factory()
}
}

[Fact]
public async Task Parameterless_pooled_factory_should_use_ConfigureDbContext_options()
{
var services = new ServiceCollection();

services.ConfigureDbContext<DefaultOptionsPooledContext>(ob => ob.UseSqlServer(SqlServerNorthwindTestStoreFactory.NorthwindConnectionString)
.EnableServiceProviderCaching(false));

services.AddPooledDbContextFactory<DefaultOptionsPooledContext>();

using var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService<IDbContextFactory<DefaultOptionsPooledContext>>();
await using var db = await factory.CreateDbContextAsync();

Assert.Equal("Microsoft.EntityFrameworkCore.SqlServer", db.Database.ProviderName);
}

[Fact]
public async Task Parameterless_pooled_factory_with_custom_pool_size_should_still_resolve()
{
var services = new ServiceCollection();

services.ConfigureDbContext<DefaultOptionsPooledContext>(ob => ob.UseSqlServer(SqlServerNorthwindTestStoreFactory.NorthwindConnectionString)
.EnableServiceProviderCaching(false));

services.AddPooledDbContextFactory<DefaultOptionsPooledContext>(poolSize: 256);

using var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService<IDbContextFactory<DefaultOptionsPooledContext>>();
await using var db = await factory.CreateDbContextAsync();

Assert.Equal("Microsoft.EntityFrameworkCore.SqlServer", db.Database.ProviderName);
Assert.Equal(
256,
db.GetService<IDbContextOptions>().FindExtension<CoreOptionsExtension>()!.MaxPoolSize);
}

private class BadCtorContext : DbContext;

[ConditionalFact]
Expand Down Expand Up @@ -1447,6 +1484,7 @@ public async Task Double_dispose_with_standalone_lease_does_not_enter_pool_twice
: BuildServiceProvider<PooledContext>();

var pool = serviceProvider.GetRequiredService<IDbContextPool<PooledContext>>();
Assert.Same(pool, serviceProvider.GetRequiredService<IDbContextPool<PooledContext>>());
var lease = new DbContextLease(pool, standalone: true);
var context = (PooledContext)lease.Context;
((IDbContextPoolable)context).SetLease(lease);
Expand Down
Loading