Skip to content

数据初始化与种子数据(Seed Data)

本页介绍 Ape‑Volo‑Admin 的数据库初始化与种子数据机制:启动时如何自动建库/建表、如何按顺序写入初始数据、如何初始化日志库与租户库,以及配置开关、幂等与常见问题。


总览

  • 自动种子数据中间件:DataSeederMiddlewareUseDataSeederMiddleware
  • 具体初始化逻辑:SeedService(主库/日志库/租户库)
  • 种子文件位置:Ape.Volo.Api/wwwroot/resources/db/{TableName}.json
  • ORM:SqlSugar CodeFirst 建库建表,支持分表 SplitTableAttribute
  • 多租户:支持 ID 隔离与独立库(TenantType.Id / TenantType.Db

启用方式

在应用启动时调用中间件:

csharp
app.UseDataSeederMiddleware();

触发条件(来自 SystemOptions / TenantOptions):

  • SystemOptions.IsInitTable = true 时执行建库建表;
  • SystemOptions.IsInitData = true 时写入主库种子数据;
  • SystemOptions.LogDataBase 指定日志库连接名,用于日志库初始化;
  • TenantOptions.Enabled = trueTenantOptions.Type == TenantType.Db 时初始化每个租户的独立数据库与表。

建议:生产环境默认关闭 IsInitTable/IsInitData,在正式变更流程中使用迁移/脚本。


主库初始化(InitMasterDataAsync)

步骤:

  1. 打印环境信息与主库连接信息;
  2. 若非 Oracle,调用 DbMaintenance.CreateDatabase() 建库;
  3. 扫描实体并建表:
    • 选择规则:带 [SugarTable] 的实体;排除实现了 ITenantEntity、带 LogDataBaseAttributeMultiDbTenantAttribute 的实体;
    • TenantOptions.Enabled && TenantOptions.Type == TenantType.Id,则额外包含实现了 ITenantEntity 的实体;
    • 对带 SplitTableAttribute 的实体使用 CodeFirst.SplitTables();否则 CodeFirst.InitTables()
  4. IsInitData = true:按顺序导入种子数据(仅当对应表暂无数据时)。

已内置的种子顺序(节选):

  • 用户 User、角色 Role、菜单 Menu、部门 Department、岗位 Job
  • 系统设置 Setting
  • 字典 Dict 与字典详情 DictDetail
  • 定时任务 QuartzNet
  • 邮箱账户 EmailAccount、邮件模板 EmailMessageTemplate
  • 关系表:UserRoleUserJobRoleMenuRoleApis
  • 接口 Apis
  • 租户 Tenant

幂等保证:每个类别插入前均执行 Queryable<T>().AnyAsync() 检查,无数据才导入。

种子文件:wwwroot/resources/db/{TableName}.json

json
// 以 sys_user 表为例(字段请与实体保持一致)
[
  {
    "Id": 1,
    "UserName": "admin",
    "NickName": "管理员",
    "CreateTime": "2024-01-01 00:00:00"
  }
]

反序列化设置(要点):

  • 日期格式:yyyy-MM-dd HH:mm:ss
  • 忽略空值
  • 自定义 CurrentDateTimeConverter

日志库初始化(InitLogData)

  • 通过 SystemOptions.LogDataBase 获取日志库连接;若未配置抛出异常:请在 appsettings.jsonDataConnection 节点配置。
  • 创建日志库(非 Oracle);
  • 选择带 LogDataBaseAttribute 的实体并建表;
  • 支持带日期占位符的表名:{year}{month}{day},示例将 {day} 固定为 01 建立当月分表;
  • 分表实体同样使用 SplitTables()

租户库初始化(InitTenantDataAsync)

  • 仅当多租户启用且类型为独立库(TenantType.Db)时执行;
  • 遍历 Tenant 表中 TenantType=Db 的租户:
    • 构建/添加租户连接(SQLite 场景将相对路径拼接为 DataSource={ContentRoot}/path);
    • 创建库并为标注 MultiDbTenantAttribute 的实体建表;
  • 每个租户库独立初始化,不导入主库的种子数据(除非你另行扩展)。

配置示例(建议放在 appsettings.Development.json

json
{
  "System": {
    "IsInitTable": true,
    "IsInitData": true,
    "IsQuickDebug": true,
    "LogDataBase": "LogDb"
  },
  "Tenant": {
    "Enabled": false,
    "Type": "Id" // 或 "Db"
  }
}

开发期可打开建库/数据开关;生产期请关闭并采用受控变更。


最佳实践与注意事项

  • 安全开关:生产环境将 IsInitTable/IsInitData 设为 false,避免二次初始化;
  • 备份优先:对已有库操作前先备份;
  • 顺序依赖:种子顺序已考虑外键/依赖关系,扩展种子时保持一致(先主表、后关系表);
  • 幂等与唯一索引:若手动导入过数据,确保唯一键与种子不冲突;
  • 大表/分表:为日志等大表启用分表策略与归档;
  • SQLite 路径:租户库的 SQLite 连接会拼接 ContentRootPath,注意读写权限;
  • 性能:大批量导入可调整批量提交、关闭多余日志(RecordSqlEnabled=false)。

常见问题(FAQ)

  • 为什么没有执行建库/导入数据?
    • 检查 System.IsInitTable/IsInitData 是否为 true;
    • 确认已在启动时调用 app.UseDataSeederMiddleware()
    • 查看应用日志是否有异常(连接权限/路径等)。
  • 提示“未配置日志数据库”?
    • appsettings.jsonDataConnection 节点添加 LogDataBase 对应连接,并在 System.LogDataBase 指定连接名。
  • Oracle 不支持建库?
    • 是预期行为:请手动建库后再启动(代码内对 Oracle 抛出提示)。
  • 多租户为 ID 隔离时是否会把 ITenantEntity 表建在主库?
    • 是的,TenantType.Id 会将实现 ITenantEntity 的实体也建在主库。

版权所有 © 2021-2026 ApeVolo-Team