diff --git a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs index da9c2e59ef..ded5be40fd 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs @@ -1,11 +1,13 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DistributedLocking; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Persistence.EFCore.Factories; using Umbraco.Cms.Persistence.EFCore.Locking; -using Umbraco.Cms.Persistence.EFCore.Migrations; using Umbraco.Cms.Persistence.EFCore.Scoping; namespace Umbraco.Extensions; @@ -17,6 +19,13 @@ public static class UmbracoEFCoreServiceCollectionExtensions public static IServiceCollection AddUmbracoEFCoreContext(this IServiceCollection services, DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction = null) where T : DbContext { + var optionsBuilder = new DbContextOptionsBuilder(); + services.TryAddSingleton>( + sp => + { + SetupDbContext(defaultEFCoreOptionsAction, sp, optionsBuilder); + return new UmbracoPooledDbContextFactory(sp.GetRequiredService(),optionsBuilder.Options); + }); services.AddPooledDbContextFactory((provider, builder) => SetupDbContext(defaultEFCoreOptionsAction, provider, builder)); services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); @@ -39,6 +48,13 @@ public static class UmbracoEFCoreServiceCollectionExtensions connectionString = connectionString.Replace(Constants.System.DataDirectoryPlaceholder, dataDirectory); } + var optionsBuilder = new DbContextOptionsBuilder(); + services.TryAddSingleton>( + sp => + { + SetupDbContext(defaultEFCoreOptionsAction, sp, optionsBuilder); + return new UmbracoPooledDbContextFactory(sp.GetRequiredService(),optionsBuilder.Options); + }); services.AddPooledDbContextFactory(options => defaultEFCoreOptionsAction?.Invoke(options, providerName, connectionString)); services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); diff --git a/src/Umbraco.Cms.Persistence.EFCore/Factories/UmbracoPooledDbContextFactory.cs b/src/Umbraco.Cms.Persistence.EFCore/Factories/UmbracoPooledDbContextFactory.cs new file mode 100644 index 0000000000..de0f7db200 --- /dev/null +++ b/src/Umbraco.Cms.Persistence.EFCore/Factories/UmbracoPooledDbContextFactory.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Persistence.EFCore.Factories; + +/// +internal class UmbracoPooledDbContextFactory : PooledDbContextFactory + where TContext : DbContext +{ + private readonly IRuntimeState _runtimeState; + private readonly DbContextOptions _options; + + /// + public UmbracoPooledDbContextFactory(IRuntimeState runtimeState, DbContextOptions options, int poolSize = 1024 /*DbContextPool.DefaultPoolSize*/) : base(options, poolSize) + { + _runtimeState = runtimeState; + _options = options; + } + + /// + public override TContext CreateDbContext() + { + if (_runtimeState.Level == RuntimeLevel.Run) + { + return base.CreateDbContext(); + } + else + { + return (TContext?)Activator.CreateInstance(typeof(TContext), _options) ?? throw new InvalidOperationException("Unable to create DbContext"); + } + } + + /// + public override async Task CreateDbContextAsync(CancellationToken cancellationToken = default) + { + if (_runtimeState.Level == RuntimeLevel.Run) + { + return await base.CreateDbContextAsync(cancellationToken); + } + else + { + return (TContext?)Activator.CreateInstance(typeof(TContext), _options) ?? throw new InvalidOperationException("Unable to create DbContext"); + } + } +}