feat: Let the DbContext handle the connection to the database (#14674)

This commit is contained in:
Nikolaj Brask-Nielsen
2023-08-24 10:15:18 +02:00
committed by GitHub
parent 7b336c45f7
commit 3f6ebe7656
2 changed files with 60 additions and 45 deletions

View File

@@ -17,8 +17,6 @@ public static class UmbracoEFCoreServiceCollectionExtensions
public static IServiceCollection AddUmbracoEFCoreContext<T>(this IServiceCollection services, DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction = null)
where T : DbContext
{
defaultEFCoreOptionsAction ??= DefaultOptionsAction;
services.AddPooledDbContextFactory<T>((provider, builder) => SetupDbContext(defaultEFCoreOptionsAction, provider, builder));
services.AddTransient(services => services.GetRequiredService<IDbContextFactory<T>>().CreateDbContext());
@@ -31,38 +29,9 @@ public static class UmbracoEFCoreServiceCollectionExtensions
return services;
}
private static void SetupDbContext(DefaultEFCoreOptionsAction defaultEFCoreOptionsAction, IServiceProvider provider, DbContextOptionsBuilder builder)
{
ConnectionStrings connectionStrings = GetConnectionStringAndProviderName(provider);
IEnumerable<IMigrationProviderSetup> migrationProviders = provider.GetServices<IMigrationProviderSetup>();
IMigrationProviderSetup? migrationProvider =
migrationProviders.FirstOrDefault(x => x.ProviderName == connectionStrings.ProviderName);
migrationProvider?.Setup(builder, connectionStrings.ConnectionString);
defaultEFCoreOptionsAction(builder, connectionStrings.ConnectionString, connectionStrings.ProviderName);
}
private static ConnectionStrings GetConnectionStringAndProviderName(IServiceProvider serviceProvider)
{
string? connectionString = null;
string? providerName = null;
ConnectionStrings connectionStrings = serviceProvider.GetRequiredService<IOptionsMonitor<ConnectionStrings>>().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;
}
public static IServiceCollection AddUmbracoEFCoreContext<T>(this IServiceCollection services, string connectionString, string providerName, DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction = null)
where T : DbContext
{
defaultEFCoreOptionsAction ??= DefaultOptionsAction;
// Replace data directory
string? dataDirectory = AppDomain.CurrentDomain.GetData(Constants.System.DataDirectoryName)?.ToString();
if (string.IsNullOrEmpty(dataDirectory) is false)
@@ -70,7 +39,7 @@ public static class UmbracoEFCoreServiceCollectionExtensions
connectionString = connectionString.Replace(Constants.System.DataDirectoryPlaceholder, dataDirectory);
}
services.AddPooledDbContextFactory<T>((_, options) => defaultEFCoreOptionsAction(options, providerName, connectionString));
services.AddPooledDbContextFactory<T>(options => defaultEFCoreOptionsAction?.Invoke(options, providerName, connectionString));
services.AddTransient(services => services.GetRequiredService<IDbContextFactory<T>>().CreateDbContext());
services.AddUnique<IAmbientEFCoreScopeStack<T>, AmbientEFCoreScopeStack<T>>();
@@ -82,21 +51,23 @@ public static class UmbracoEFCoreServiceCollectionExtensions
return services;
}
private static void DefaultOptionsAction(DbContextOptionsBuilder options, string? providerName, string? connectionString)
private static void SetupDbContext(DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction, IServiceProvider provider, DbContextOptionsBuilder builder)
{
if (connectionString.IsNullOrWhiteSpace())
ConnectionStrings connectionStrings = GetConnectionStringAndProviderName(provider);
defaultEFCoreOptionsAction?.Invoke(builder, connectionStrings.ConnectionString, connectionStrings.ProviderName);
}
private static ConnectionStrings GetConnectionStringAndProviderName(IServiceProvider serviceProvider)
{
ConnectionStrings connectionStrings = serviceProvider.GetRequiredService<IOptionsMonitor<ConnectionStrings>>().CurrentValue;
// Replace data directory
string? dataDirectory = AppDomain.CurrentDomain.GetData(Constants.System.DataDirectoryName)?.ToString();
if (string.IsNullOrEmpty(dataDirectory) is false)
{
return;
connectionStrings.ConnectionString = connectionStrings.ConnectionString?.Replace(Constants.System.DataDirectoryPlaceholder, dataDirectory);
}
switch (providerName)
{
case "Microsoft.Data.Sqlite":
options.UseSqlite(connectionString);
break;
case "Microsoft.Data.SqlClient":
options.UseSqlServer(connectionString);
break;
}
return connectionStrings;
}
}

View File

@@ -1,9 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Persistence.EFCore.Migrations;
namespace Umbraco.Cms.Persistence.EFCore;
/// <summary>
/// Represents the Umbraco EF Core database context.
/// </summary>
/// <remarks>
/// To autogenerate migrations use the following commands
/// and insure the 'src/Umbraco.Web.UI/appsettings.json' have a connection string set with the right provider.
@@ -23,9 +32,44 @@ namespace Umbraco.Cms.Persistence.EFCore;
/// </remarks>
public class UmbracoDbContext : DbContext
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoDbContext"/> class.
/// </summary>
/// <param name="options"></param>
public UmbracoDbContext(DbContextOptions<UmbracoDbContext> options)
: base(options)
: base(ConfigureOptions(options, out IOptionsMonitor<ConnectionStrings>? connectionStringsOptionsMonitor))
{
connectionStringsOptionsMonitor.OnChange(c =>
{
ILogger<UmbracoDbContext> logger = StaticServiceProvider.Instance.GetRequiredService<ILogger<UmbracoDbContext>>();
logger.LogWarning("Connection string changed, disposing context");
Dispose();
});
}
private static DbContextOptions<UmbracoDbContext> ConfigureOptions(DbContextOptions<UmbracoDbContext> options, out IOptionsMonitor<ConnectionStrings> connectionStringsOptionsMonitor)
{
connectionStringsOptionsMonitor = StaticServiceProvider.Instance.GetRequiredService<IOptionsMonitor<ConnectionStrings>>();
ConnectionStrings connectionStrings = connectionStringsOptionsMonitor.CurrentValue;
if (string.IsNullOrWhiteSpace(connectionStrings.ConnectionString))
{
ILogger<UmbracoDbContext> logger = StaticServiceProvider.Instance.GetRequiredService<ILogger<UmbracoDbContext>>();
logger.LogCritical("No connection string was found, cannot setup Umbraco EF Core context");
}
IEnumerable<IMigrationProviderSetup> migrationProviders = StaticServiceProvider.Instance.GetServices<IMigrationProviderSetup>();
IMigrationProviderSetup? migrationProvider = migrationProviders.FirstOrDefault(x => x.ProviderName == connectionStrings.ProviderName);
if (migrationProvider == null && connectionStrings.ProviderName != null)
{
throw new InvalidOperationException($"No migration provider found for provider name {connectionStrings.ProviderName}");
}
var optionsBuilder = new DbContextOptionsBuilder<UmbracoDbContext>(options);
migrationProvider?.Setup(optionsBuilder, connectionStrings.ConnectionString);
return optionsBuilder.Options;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)