Skip to content

Commit

Permalink
Make PooledDbContextFactory public
Browse files Browse the repository at this point in the history
Closes #24137
  • Loading branch information
roji committed Apr 21, 2021
1 parent 277b43a commit 2de6a4e
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 193 deletions.
101 changes: 28 additions & 73 deletions src/EFCore/Extensions/EntityFrameworkServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// </param>
/// <param name="contextLifetime"> The lifetime with which to register the DbContext service in the container. </param>
/// <param name="optionsLifetime"> The lifetime with which to register the DbContextOptions service in the container. </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContext<TContext>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder>? optionsAction = null,
Expand Down Expand Up @@ -102,9 +100,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// </param>
/// <param name="contextLifetime"> The lifetime with which to register the DbContext service in the container. </param>
/// <param name="optionsLifetime"> The lifetime with which to register the DbContextOptions service in the container. </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContext<TContextService, TContextImplementation>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder>? optionsAction = null,
Expand Down Expand Up @@ -149,16 +145,12 @@ public static class EntityFrameworkServiceCollectionExtensions
/// will not be called.
/// </para>
/// </param>
/// <param name="poolSize">
/// Sets the maximum number of instances retained by the pool.
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <param name="poolSize"> Sets the maximum number of instances retained by the pool. Defaults to 128. </param>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextPool<TContext>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder> optionsAction,
int poolSize = 128)
int poolSize = DbContextPool<DbContext>.DefaultPoolSize)
where TContext : DbContext
=> AddDbContextPool<TContext, TContext>(serviceCollection, optionsAction, poolSize);

Expand Down Expand Up @@ -195,16 +187,12 @@ public static class EntityFrameworkServiceCollectionExtensions
/// will not be called.
/// </para>
/// </param>
/// <param name="poolSize">
/// Sets the maximum number of instances retained by the pool.
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <param name="poolSize"> Sets the maximum number of instances retained by the pool. Defaults to 128. </param>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextPool<TContextService, TContextImplementation>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder> optionsAction,
int poolSize = 128)
int poolSize = DbContextPool<DbContext>.DefaultPoolSize)
where TContextImplementation : DbContext, TContextService
where TContextService : class
{
Expand Down Expand Up @@ -253,16 +241,12 @@ public static class EntityFrameworkServiceCollectionExtensions
/// will not be called.
/// </para>
/// </param>
/// <param name="poolSize">
/// Sets the maximum number of instances retained by the pool.
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <param name="poolSize"> Sets the maximum number of instances retained by the pool. Defaults to 128. </param>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextPool<TContext>(
this IServiceCollection serviceCollection,
Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,
int poolSize = 128)
int poolSize = DbContextPool<DbContext>.DefaultPoolSize)
where TContext : DbContext
=> AddDbContextPool<TContext, TContext>(serviceCollection, optionsAction, poolSize);

Expand Down Expand Up @@ -308,16 +292,12 @@ public static class EntityFrameworkServiceCollectionExtensions
/// will not be called.
/// </para>
/// </param>
/// <param name="poolSize">
/// Sets the maximum number of instances retained by the pool.
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <param name="poolSize"> Sets the maximum number of instances retained by the pool. Defaults to 128. </param>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextPool<TContextService, TContextImplementation>(
this IServiceCollection serviceCollection,
Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,
int poolSize = 128)
int poolSize = DbContextPool<DbContext>.DefaultPoolSize)
where TContextImplementation : DbContext, TContextService
where TContextService : class
{
Expand Down Expand Up @@ -381,9 +361,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// <param name="serviceCollection"> The <see cref="IServiceCollection" /> to add services to. </param>
/// <param name="contextLifetime"> The lifetime with which to register the DbContext service in the container. </param>
/// <param name="optionsLifetime"> The lifetime with which to register the DbContextOptions service in the container. </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContext<TContext>(
this IServiceCollection serviceCollection,
ServiceLifetime contextLifetime,
Expand Down Expand Up @@ -411,9 +389,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// <param name="serviceCollection"> The <see cref="IServiceCollection" /> to add services to. </param>
/// <param name="contextLifetime"> The lifetime with which to register the DbContext service in the container. </param>
/// <param name="optionsLifetime"> The lifetime with which to register the DbContextOptions service in the container. </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContext<TContextService, TContextImplementation>(
this IServiceCollection serviceCollection,
ServiceLifetime contextLifetime,
Expand Down Expand Up @@ -470,9 +446,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// </param>
/// <param name="contextLifetime"> The lifetime with which to register the DbContext service in the container. </param>
/// <param name="optionsLifetime"> The lifetime with which to register the DbContextOptions service in the container. </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContext<TContext>(
this IServiceCollection serviceCollection,
Action<IServiceProvider, DbContextOptionsBuilder>? optionsAction,
Expand Down Expand Up @@ -527,9 +501,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// </param>
/// <param name="contextLifetime"> The lifetime with which to register the DbContext service in the container. </param>
/// <param name="optionsLifetime"> The lifetime with which to register the DbContextOptions service in the container. </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContext<TContextService, TContextImplementation>(
this IServiceCollection serviceCollection,
Action<IServiceProvider, DbContextOptionsBuilder>? optionsAction,
Expand Down Expand Up @@ -600,9 +572,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// The lifetime with which to register the factory and options.
/// The default is <see cref="ServiceLifetime.Singleton" />
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextFactory<TContext>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder>? optionsAction = null,
Expand Down Expand Up @@ -659,9 +629,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// The lifetime with which to register the factory and options.
/// The default is <see cref="ServiceLifetime.Singleton" />
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextFactory<TContext, TFactory>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder>? optionsAction = null,
Expand Down Expand Up @@ -727,9 +695,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// The lifetime with which to register the factory and options.
/// The default is <see cref="ServiceLifetime.Singleton" />
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextFactory<TContext>(
this IServiceCollection serviceCollection,
Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,
Expand Down Expand Up @@ -794,9 +760,7 @@ public static class EntityFrameworkServiceCollectionExtensions
/// The lifetime with which to register the factory and options.
/// The default is <see cref="ServiceLifetime.Singleton" />
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddDbContextFactory<TContext, TFactory>(
this IServiceCollection serviceCollection,
Action<IServiceProvider, DbContextOptionsBuilder>? optionsAction,
Expand Down Expand Up @@ -850,16 +814,12 @@ public static class EntityFrameworkServiceCollectionExtensions
/// will not be called.
/// </para>
/// </param>
/// <param name="poolSize">
/// Sets the maximum number of instances retained by the pool.
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <param name="poolSize"> Sets the maximum number of instances retained by the pool. Defaults to 128. </param>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddPooledDbContextFactory<TContext>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder> optionsAction,
int poolSize = 128)
int poolSize = DbContextPool<DbContext>.DefaultPoolSize)
where TContext : DbContext
{
Check.NotNull(optionsAction, nameof(optionsAction));
Expand Down Expand Up @@ -898,24 +858,19 @@ public static class EntityFrameworkServiceCollectionExtensions
/// will not be called.
/// </para>
/// </param>
/// <param name="poolSize">
/// Sets the maximum number of instances retained by the pool.
/// </param>
/// <returns>
/// The same service collection so that multiple calls can be chained.
/// </returns>
/// <param name="poolSize"> Sets the maximum number of instances retained by the pool. Defaults to 128. </param>
/// <returns> The same service collection so that multiple calls can be chained. </returns>
public static IServiceCollection AddPooledDbContextFactory<TContext>(
this IServiceCollection serviceCollection,
Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,
int poolSize = 128)
int poolSize = DbContextPool<DbContext>.DefaultPoolSize)
where TContext : DbContext
{
Check.NotNull(serviceCollection, nameof(serviceCollection));
Check.NotNull(optionsAction, nameof(optionsAction));

AddPoolingOptions<TContext>(serviceCollection, optionsAction, poolSize);

serviceCollection.TryAddSingleton<IDbContextPool<TContext>, DbContextPool<TContext>>();
serviceCollection.TryAddSingleton<IDbContextFactory<TContext>, PooledDbContextFactory<TContext>>();

return serviceCollection;
Expand Down
4 changes: 1 addition & 3 deletions src/EFCore/IDbContextFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// Defines a factory for creating <see cref="DbContext" /> instances.
/// A service of this type is registered in the dependency injection container by the
/// <see cref="M:EntityFrameworkServiceCollectionExtensions.AddDbContextPool" /> methods.
/// </summary>
/// <typeparam name="TContext"> The <see cref="DbContext" /> type to create. </typeparam>
public interface IDbContextFactory<out TContext>
Expand All @@ -17,7 +15,7 @@ public interface IDbContextFactory<out TContext>
/// Creates a new <see cref="DbContext" /> instance.
/// </para>
/// <para>
/// The caller is responsible for disposing the context; it will not be disposed by the dependency injection container.
/// The caller is responsible for disposing the context; it will not be disposed by any dependency injection container.
/// </para>
/// </summary>
/// <returns> A new context instance. </returns>
Expand Down
5 changes: 5 additions & 0 deletions src/EFCore/Infrastructure/CoreOptionsExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,11 @@ public virtual void ApplyServices(IServiceCollection services)
/// <param name="options"> The options being validated. </param>
public virtual void Validate(IDbContextOptions options)
{
if (MaxPoolSize.HasValue && MaxPoolSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(MaxPoolSize), CoreStrings.InvalidPoolSize);
}

if (_internalServiceProvider != null)
{
if (ReplacedServices != null)
Expand Down
52 changes: 52 additions & 0 deletions src/EFCore/Infrastructure/PooledDbContextFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.Internal;

namespace Microsoft.EntityFrameworkCore.Infrastructure
{
/// <summary>
/// <para>
/// A factory returning pooled <see cref="DbContext" /> instances. Disposing the instance returned by this factory returns
/// them to the internal pooling mechanism.
/// </para>
/// <para>
/// A service of this type is registered in the dependency injection container by the
/// <see cref="M:EntityFrameworkServiceCollectionExtensions.AddDbContextPool" /> methods.
/// </para>
/// </summary>
public class PooledDbContextFactory<TContext> : IDbContextFactory<TContext>
where TContext : DbContext
{
private readonly IDbContextPool<TContext> _pool;

/// <summary>
/// Initializes a new instance of the <see cref="PooledDbContextFactory{TContext}" /> class.
/// The maximum number of instances retained by the pool defaults to 128.
/// </summary>
/// <param name="options"> The options to use for contexts produced by this factory. </param>
public PooledDbContextFactory(DbContextOptions<TContext> options)
=> _pool = new DbContextPool<TContext>(options);

/// <summary>
/// Initializes a new instance of the <see cref="PooledDbContextFactory{TContext}" /> class.
/// </summary>
/// <param name="options"> The options to use for contexts produced by this factory. </param>
/// <param name="poolSize"> Sets the maximum number of instances retained by the pool. </param>
public PooledDbContextFactory(DbContextOptions<TContext> options, int poolSize)
{
var optionsBuilder = new DbContextOptionsBuilder<TContext>(options);

var extension = (options.FindExtension<CoreOptionsExtension>() ?? new CoreOptionsExtension())
.WithMaxPoolSize(poolSize);

((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

_pool = new DbContextPool<TContext>(optionsBuilder.Options);
}

/// <inheritdoc />
public virtual TContext CreateDbContext()
=> (TContext)new DbContextLease(_pool, standalone: true).Context;
}
}
13 changes: 12 additions & 1 deletion src/EFCore/Internal/DbContextPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ namespace Microsoft.EntityFrameworkCore.Internal
public class DbContextPool<TContext> : IDbContextPool<TContext>, IDisposable, IAsyncDisposable
where TContext : DbContext
{
private const int DefaultPoolSize = 32;
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const int DefaultPoolSize = 128;

private readonly ConcurrentQueue<IDbContextPoolable> _pool = new();

Expand All @@ -42,6 +48,11 @@ public DbContextPool(DbContextOptions<TContext> options)
{
_maxSize = options.FindExtension<CoreOptionsExtension>()?.MaxPoolSize ?? DefaultPoolSize;

if (_maxSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(CoreOptionsExtension.MaxPoolSize), CoreStrings.InvalidPoolSize);
}

options.Freeze();

_activator = CreateActivator(options);
Expand Down
Loading

0 comments on commit 2de6a4e

Please sign in to comment.