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.Scoping; namespace Umbraco.Extensions; public static class UmbracoEFCoreServiceCollectionExtensions { public delegate void DefaultEFCoreOptionsAction(DbContextOptionsBuilder options, string? providerName, string? connectionString); 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()); services.AddUnique, AmbientEFCoreScopeStack>(); services.AddUnique, EFCoreScopeAccessor>(); services.AddUnique, EFCoreScopeProvider>(); services.AddSingleton>(); services.AddSingleton>(); return services; } public static IServiceCollection AddUmbracoEFCoreContext(this IServiceCollection services, string connectionString, string providerName, DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction = null) where T : DbContext { // Replace data directory string? dataDirectory = AppDomain.CurrentDomain.GetData(Constants.System.DataDirectoryName)?.ToString(); if (string.IsNullOrEmpty(dataDirectory) is false) { 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()); services.AddUnique, AmbientEFCoreScopeStack>(); services.AddUnique, EFCoreScopeAccessor>(); services.AddUnique, EFCoreScopeProvider>(); services.AddSingleton>(); services.AddSingleton>(); return services; } private static void SetupDbContext(DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction, IServiceProvider provider, DbContextOptionsBuilder builder) { ConnectionStrings connectionStrings = GetConnectionStringAndProviderName(provider); defaultEFCoreOptionsAction?.Invoke(builder, connectionStrings.ConnectionString, connectionStrings.ProviderName); } private static ConnectionStrings GetConnectionStringAndProviderName(IServiceProvider serviceProvider) { ConnectionStrings connectionStrings = serviceProvider.GetRequiredService>().CurrentValue; // Replace data directory string? dataDirectory = AppDomain.CurrentDomain.GetData(Constants.System.DataDirectoryName)?.ToString(); if (string.IsNullOrEmpty(dataDirectory) is false) { connectionStrings.ConnectionString = connectionStrings.ConnectionString?.Replace(Constants.System.DataDirectoryPlaceholder, dataDirectory); } return connectionStrings; } }