FileShare/FileShare-EFCore/Database/DatabaseManager.cs

226 lines
8.2 KiB
C#
Raw Normal View History

2026-05-22 14:29:22 +08:00
using FileShare_Common.Infrastructure;
2026-05-21 15:52:36 +08:00
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
2026-05-22 14:29:22 +08:00
namespace FileShare_EFCore.Database
2026-05-21 15:52:36 +08:00
{
/// <summary>
/// 数据库管理器 —— 负责连接测试、自动迁移、种子数据、版本检查。
/// 在应用启动时调用,确保数据库结构与应用代码同步。
/// </summary>
public class DatabaseManager<TContext> where TContext : AppDbContext
{
/// <summary>
/// 数据库上下文实例。
/// </summary>
private readonly TContext _context;
/// <summary>
/// 数据库配置。
/// </summary>
private readonly DatabaseConfiguration _config;
/// <summary>
/// DI 服务提供程序(可选,用于种子数据中解析服务)。
/// </summary>
private readonly IServiceProvider? _serviceProvider;
/// <summary>
/// 初始化数据库管理器。
/// </summary>
/// <param name="context">数据库上下文。</param>
/// <param name="config">数据库配置。</param>
/// <param name="serviceProvider">可选的 DI 容器。</param>
public DatabaseManager(TContext context, DatabaseConfiguration config, IServiceProvider? serviceProvider = null)
{
_context = context;
_config = config;
_serviceProvider = serviceProvider;
}
/// <summary>
/// 初始化数据库:测试连接 → 自动迁移 → 种子数据。
/// </summary>
public async Task InitializeAsync(Action<TContext, IServiceProvider?>? seeder = null)
{
AppLog.Information(
"正在初始化数据库 Provider={Provider}, AppVersion={AppVersion}",
_config.Provider,
GetApplicationVersion());
// 1. 自动迁移如果启用。MigrateAsync 会按迁移历史顺序执行全部待处理迁移,
// 支持用户从较旧软件版本直接升级到当前版本。
if (_config.AutoMigrate)
{
if (_config.RecreateDatabase)
{
AppLog.Warning(
"RecreateDatabase=true将删除并重建当前连接指向的数据库。Provider={Provider}",
_config.Provider);
await _context.Database.EnsureDeletedAsync();
}
await MigrateAsync();
}
else
{
var canConnect = await CanConnectAsync();
if (!canConnect)
{
throw new InvalidOperationException(
$"无法连接到数据库 [{_config.Provider}],请检查连接字符串和数据库服务状态。");
}
}
// 2. 种子数据
if (seeder != null)
{
seeder(_context, _serviceProvider);
await _context.SaveChangesAsync();
}
}
/// <summary>
/// 测试数据库连接是否正常。
/// </summary>
public async Task<bool> CanConnectAsync()
{
try
{
return await _context.Database.CanConnectAsync();
}
catch (Exception ex)
2026-05-21 15:52:36 +08:00
{
AppLog.Warning("数据库连接测试失败 Provider={Provider} Error={Error}", _config.Provider, ex.Message);
2026-05-21 15:52:36 +08:00
return false;
}
}
/// <summary>
/// 执行待处理的迁移。
/// 使用 EF Core 原生迁移机制,自动检测并应用 Schema 变更。
/// </summary>
public async Task MigrateAsync()
{
try
{
var appliedMigrations = (await _context.Database.GetAppliedMigrationsAsync()).ToList();
var pendingMigrations = await _context.Database.GetPendingMigrationsAsync();
if (pendingMigrations.Any())
{
if (appliedMigrations.Count == 0)
{
AppLog.Information(
"未检测到已应用迁移,将按当前 Provider={Provider} 从 0 构建完整表结构",
_config.Provider);
}
AppLog.Information(
"当前已应用 {AppliedCount} 个迁移,检测到 {PendingCount} 个待执行迁移: {Migrations}",
appliedMigrations.Count,
pendingMigrations.Count(),
string.Join(", ", pendingMigrations));
await _context.Database.MigrateAsync();
AppLog.Information("数据库迁移完成({Count} 个迁移已应用)", pendingMigrations.Count());
}
else
{
AppLog.Information("数据库已是最新版本,无需迁移");
}
}
catch (Exception ex)
{
AppLog.Error(ex, "数据库迁移失败");
throw;
}
}
/// <summary>
/// 获取当前应用程序的版本号,优先读取 AssemblyInformationalVersion回退到 AssemblyVersion。
/// </summary>
/// <returns>应用程序版本字符串。</returns>
private static string GetApplicationVersion()
{
var assembly = Assembly.GetEntryAssembly() ?? typeof(TContext).Assembly;
return assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion
?? assembly.GetName().Version?.ToString()
?? "unknown";
}
/// <summary>
/// 获取数据库当前版本信息。
/// </summary>
public async Task<DatabaseVersionInfo> GetVersionInfoAsync()
{
var appliedMigrations = await _context.Database.GetAppliedMigrationsAsync();
var pendingMigrations = await _context.Database.GetPendingMigrationsAsync();
return new DatabaseVersionInfo
{
Provider = _config.Provider.ToString(),
AppliedMigrations = appliedMigrations.ToList(),
PendingMigrations = pendingMigrations.ToList(),
IsLatest = !pendingMigrations.Any(),
CanConnect = await CanConnectAsync(),
};
}
/// <summary>
/// 生成从指定迁移到最新版本的 SQL 脚本(用于生产环境审计)。
/// </summary>
public string GenerateMigrationScript(string? fromMigration = null)
{
var migrator = _context.GetService<IMigrator>();
return fromMigration is null
? migrator.GenerateScript()
: migrator.GenerateScript(fromMigration);
}
/// <summary>
/// 确保数据库已创建(不执行迁移,适用于简单场景)。
/// </summary>
public bool EnsureCreated()
{
return _context.Database.EnsureCreated();
}
}
/// <summary>
/// 数据库版本信息 DTO。
/// </summary>
public class DatabaseVersionInfo
{
/// <summary>
/// 获取或设置数据库提供程序名称。
/// </summary>
public string Provider { get; set; } = string.Empty;
/// <summary>
/// 获取或设置已应用的迁移列表。
/// </summary>
public List<string> AppliedMigrations { get; set; } = new();
/// <summary>
/// 获取或设置待应用的迁移列表。
/// </summary>
public List<string> PendingMigrations { get; set; } = new();
/// <summary>
/// 获取或设置是否为最新版本。
/// </summary>
public bool IsLatest { get; set; }
/// <summary>
/// 获取或设置数据库是否可连接。
/// </summary>
public bool CanConnect { get; set; }
}
}