2026-05-11 14:35:34 +08:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Avalonia_EFCore.Database
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 应用数据库上下文基类 —— 自动根据 DatabaseConfiguration 选择数据库提供程序。
|
|
|
|
|
|
/// 所有业务 DbContext 继承此类即可获得多数据库支持。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public abstract class AppDbContext(DatabaseConfiguration dbConfig) : DbContext
|
|
|
|
|
|
{
|
2026-05-18 11:35:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据库配置。
|
|
|
|
|
|
/// </summary>
|
2026-05-11 14:35:34 +08:00
|
|
|
|
private readonly DatabaseConfiguration _dbConfig = dbConfig;
|
|
|
|
|
|
|
2026-05-18 11:35:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 配置数据库提供程序和连接选项。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="optionsBuilder">选项构建器。</param>
|
2026-05-11 14:35:34 +08:00
|
|
|
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (optionsBuilder.IsConfigured) return;
|
|
|
|
|
|
|
|
|
|
|
|
ConfigureProvider(optionsBuilder, _dbConfig);
|
|
|
|
|
|
|
|
|
|
|
|
if (_dbConfig.EnableDetailedLog)
|
|
|
|
|
|
{
|
|
|
|
|
|
optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 启用详细的 EF Core 错误信息
|
|
|
|
|
|
optionsBuilder.EnableDetailedErrors();
|
|
|
|
|
|
optionsBuilder.EnableSensitiveDataLogging(_dbConfig.EnableDetailedLog);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据配置选择数据库提供程序。
|
|
|
|
|
|
/// 使用注册模式,由宿主项目注册具体的提供程序实现。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static void ConfigureProvider(DbContextOptionsBuilder optionsBuilder, DatabaseConfiguration config)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (DatabaseProviderRegistry.TryGet(config.Provider, out var configurator))
|
|
|
|
|
|
{
|
|
|
|
|
|
configurator(optionsBuilder, config.ConnectionString, config.Timeout);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new NotSupportedException(
|
|
|
|
|
|
$"数据库提供程序 {config.Provider} 未注册。" +
|
|
|
|
|
|
$"请在宿主项目中安装对应的 EF Core NuGet 包并调用 DatabaseProviderRegistry.Register()。");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
optionsBuilder.EnableDetailedErrors();
|
2026-05-15 15:26:46 +08:00
|
|
|
|
optionsBuilder.EnableSensitiveDataLogging(config.EnableDetailedLog);
|
|
|
|
|
|
|
|
|
|
|
|
if (config.EnableDetailedLog)
|
|
|
|
|
|
{
|
|
|
|
|
|
optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
|
|
|
|
|
|
}
|
2026-05-11 14:35:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 保存时自动设置时间戳。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetTimestamps();
|
|
|
|
|
|
return base.SaveChanges(acceptAllChangesOnSuccess);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 11:35:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 异步保存更改,自动设置时间戳。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="acceptAllChangesOnSuccess">是否在成功时接受所有更改。</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌。</param>
|
|
|
|
|
|
/// <returns>受影响的行数。</returns>
|
2026-05-11 14:35:34 +08:00
|
|
|
|
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetTimestamps();
|
|
|
|
|
|
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 11:35:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 自动设置新增或修改实体的 CreatedAt 和 UpdatedAt 时间戳。
|
|
|
|
|
|
/// </summary>
|
2026-05-11 14:35:34 +08:00
|
|
|
|
private void SetTimestamps()
|
|
|
|
|
|
{
|
|
|
|
|
|
var entries = ChangeTracker.Entries()
|
|
|
|
|
|
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var entry in entries)
|
|
|
|
|
|
{
|
|
|
|
|
|
var entity = entry.Entity;
|
|
|
|
|
|
|
|
|
|
|
|
// 使用反射设置 CreatedAt / UpdatedAt(如果存在)
|
|
|
|
|
|
var createdAtProp = entity.GetType().GetProperty("CreatedAt");
|
|
|
|
|
|
var updatedAtProp = entity.GetType().GetProperty("UpdatedAt");
|
|
|
|
|
|
|
|
|
|
|
|
if (entry.State == EntityState.Added && createdAtProp != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
createdAtProp.SetValue(entity, DateTime.UtcNow);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (updatedAtProp != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
updatedAtProp.SetValue(entity, DateTime.UtcNow);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|