using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using TakeoutSaaS.Infrastructure.Common.Options; using TakeoutSaaS.Infrastructure.Common.Persistence; using TakeoutSaaS.Shared.Abstractions.Data; namespace TakeoutSaaS.Infrastructure.Common.Extensions; /// /// 数据访问与多数据源相关的服务注册扩展。 /// public static class DatabaseServiceCollectionExtensions { /// /// 注册数据库基础设施(多数据源配置、读写分离、Dapper 执行器)。 /// /// 服务集合。 /// 配置源。 /// 服务集合。 public static IServiceCollection AddDatabaseInfrastructure(this IServiceCollection services, IConfiguration configuration) { services.AddOptions() .Bind(configuration.GetSection(DatabaseOptions.SectionName)) .ValidateDataAnnotations() .ValidateOnStart(); services.AddSingleton(); services.AddScoped(); return services; } /// /// 为指定 DbContext 注册读写分离的 PostgreSQL 配置,同时提供读上下文工厂。 /// /// 上下文类型。 /// 服务集合。 /// 逻辑数据源名称。 /// 服务集合。 public static IServiceCollection AddPostgresDbContext( this IServiceCollection services, string dataSourceName) where TContext : DbContext { services.AddDbContext( (sp, options) => { ConfigureDbContextOptions(sp, options, dataSourceName, DatabaseConnectionRole.Write); }, contextLifetime: ServiceLifetime.Scoped, optionsLifetime: ServiceLifetime.Singleton); services.AddDbContextFactory((sp, options) => { ConfigureDbContextOptions(sp, options, dataSourceName, DatabaseConnectionRole.Read); }); return services; } /// /// 配置 DbContextOptions,应用连接串、命令超时与重试策略。 /// /// 服务提供程序。 /// 上下文配置器。 /// 数据源名称。 /// 连接角色。 private static void ConfigureDbContextOptions( IServiceProvider serviceProvider, DbContextOptionsBuilder optionsBuilder, string dataSourceName, DatabaseConnectionRole role) { var connection = serviceProvider .GetRequiredService() .GetConnection(dataSourceName, role); optionsBuilder.UseNpgsql( connection.ConnectionString, npgsqlOptions => { npgsqlOptions.CommandTimeout(connection.CommandTimeoutSeconds); npgsqlOptions.EnableRetryOnFailure( connection.MaxRetryCount, TimeSpan.FromSeconds(connection.MaxRetryDelaySeconds), null); }); } }