using System; using System.Collections.Generic; using APT.Infrastructure.Api; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; namespace APT.Infrastructure.EF { public static class TenantCoreExtension { public static IServiceCollection AddDbPerConnection(this IServiceCollection services, DbIntegrationType dbType, string key = "default", string connectionPrefix = "tenanted", Action optionAction = null, Action dbContextSetup = null) where TDbContext : DbContext, ITenantDbContext { var settings = new TenantSettings() { Key = key, DbType = dbType, ConnectionPrefix = connectionPrefix, ConnectionType = ConnectionResolverType.ByDatabase, DbContextOptionAction = optionAction, DbContextSetup = dbContextSetup }; return services.AddTenantedDatabase(settings); } public static IServiceCollection AddDbPerConnection(this IServiceCollection services, Action> setupAction = null) where TDbContext : DbContext, ITenantDbContext { var settings = new TenantSettings() { ConnectionType = ConnectionResolverType.ByDatabase }; return services.AddTenantedDatabase(settings, setupAction); } public static IServiceCollection AddDbPerTable(this IServiceCollection services, DbIntegrationType dbType, string key = "default", string connectionName = "tenantConnection", Action optionAction = null, Action dbContextSetup = null) where TDbContext : DbContext, ITenantDbContext { var settings = new TenantSettings() { Key = key, DbType = dbType, ConnectionName = connectionName, ConnectionType = ConnectionResolverType.ByTable, DbContextOptionAction = optionAction, DbContextSetup = dbContextSetup }; return services.AddTenantedDatabase(settings); } public static IServiceCollection AddDbPerTable(this IServiceCollection services, Action> setupAction = null) where TDbContext : DbContext, ITenantDbContext { var settings = new TenantSettings() { ConnectionType = ConnectionResolverType.ByTable }; return services.AddTenantedDatabase(settings, setupAction); } public static IServiceCollection AddDbPerSchema(this IServiceCollection services, DbIntegrationType dbType, string key = "default", string connectionName = "tenantConnection", Action optionAction = null, Action dbContextSetup = null) where TDbContext : DbContext, ITenantDbContext { var settings = new TenantSettings() { Key = key, DbType = dbType, ConnectionName = connectionName, ConnectionType = ConnectionResolverType.BySchema, DbContextOptionAction = optionAction, DbContextSetup = dbContextSetup }; return services.AddTenantedDatabase(settings); } public static IServiceCollection AddDbPerSchema(this IServiceCollection services, Action> setupAction = null) where TDbContext : DbContext, ITenantDbContext { var settings = new TenantSettings() { ConnectionType = ConnectionResolverType.BySchema }; return services.AddTenantedDatabase(settings, setupAction); } public static IServiceCollection AddTenantedDatabase(this IServiceCollection services, TenantSettings settings, Action> setupAction = null) where TDbContext : DbContext, ITenantDbContext { services.AddScoped(); services.AddScoped(); services.TryAddScoped(); services.TryAddScoped, TenantConnectionResolver>(); services.TryAddScoped, TenantEntityBuilder>(); services.TryAddScoped, SimpleEntityScaner>(); services.AddScoped(); services.InitSettings(settings, setupAction); services.AddTenantDbContext(); return services; } internal static IServiceCollection AddTenantDbContext(this IServiceCollection services) where TDbContext : DbContext, ITenantDbContext { services.AddDbContext((serviceProvider, options) => { var settings = serviceProvider.GetService>(); var connectionResolver = serviceProvider.GetService>(); var tenant = serviceProvider.GetService(); settings.DbContextSetup?.Invoke(serviceProvider, connectionResolver.GetConnection(), options); options.ReplaceServiceTenanted(settings); settings.DbContextOptionAction?.Invoke(options); }); return services; } internal static IServiceCollection InitSettings(this IServiceCollection services, TenantSettings settings, Action> setupAction) where TDbContext : DbContext, ITenantDbContext { services.AddSingleton((sp) => { var rct = settings ?? new TenantSettings(); setupAction?.Invoke(rct); return rct; }); return services; } public static void ReplaceServiceTenanted(this DbContextOptionsBuilder dbOptions, TenantSettings settings) where TDbContext : DbContext, ITenantDbContext { if (Constants.specialConnectionTypes.Contains(settings.ConnectionType)) { dbOptions.ReplaceService>(); dbOptions.ReplaceService(); } } public static void TenantBuilderSetup(this RelationalDbContextOptionsBuilder builder, IServiceProvider serviceProvider, TenantSettings settings, TenantInfo tenant) where TDbContext : DbContext, ITenantDbContext where TBuilder : RelationalDbContextOptionsBuilder where TExtension : RelationalOptionsExtension, new() { if (settings.ConnectionType == ConnectionResolverType.ByTable) { builder.MigrationsHistoryTable($"{tenant.Name}__EFMigrationsHistory"); } if (settings.ConnectionType == ConnectionResolverType.BySchema) { builder.MigrationsHistoryTable("__EFMigrationHistory", $"{(settings.SchemaFunc?.Invoke(tenant) ?? (tenant?.Name ?? "dbo"))}"); } } } }