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);
});
}
}