Skip to content
bitzorcas
中EN

Reference

Master Data 记录、元数据与 ORM 边界

九种 CatalogRecord 的表、租户/软删、自然键、编译期元数据、IEntitySet 与双 ORM 所有权设计。

Last updated

MasterData 的“模型”是九种 owner-local persistence record,不是领域聚合。它们保留旧表兼容,同时通过编译期元数据服务不同 ORM。

1. 为什么叫 CatalogRecord

*CatalogRecord 明确表达它是参考目录持久化行,并避免恢复旧的一表一 *Entity + mapper 模式。所有记录直接携带 [BitzTable]、[BitzColumn] 与 [BitzIndex],Generator 在编译期生成表模型。

CatalogRecord + Bitz attributes

Persistence Metadata Generator

Generated metadata manifest

SqlSugar adapter

EF Core adapter

同一物理表

MasterData 程序集本身不引用两个 adapter。

2. 共同基线

九种行都继承 BizEntityBase,继承 ID、TenantId、OfficeId、审计、启用、软删、内部标记等字段;所有表都声明租户过滤和软删除。

这有两个后果:

  • “平台标准数据”也以 tenant 行呈现,当前资产多使用 TenantId=0;
  • 查询必须依赖 adapter 正确应用租户/软删过滤,不能绕过 IEntitySet<T> 裸查。

3. 自然键与索引

表关键索引/自然键注意事项
SysLanguageTenant + Code unique应验证每租户最多一个默认语言
SysCountryTenant + Alpha2 uniqueAlpha2 属性却可 null
SysExchangeRateTenant + base + target + ModifyTimee unique字段拼写与 CSV 不一致
SysGeneralCodeGroupTenant + Code unique层级无 FK/环约束
SysGeneralCodeTenant + Code + Class uniqueResolver 按 Group 读取
SysGeneralCodeTextTenant + Code + Class + Language index该索引未标 unique
SysIndustrySettingTenant + ParentCode indexCode 没有 unique 索引
SysPublicHolidayTenant + Country + Year indexDayDate 自然键仅在 seeder 中
SysTranslationKey + Lang + Scope + Tenant + Office unique由 I18n 使用

数据库约束与 seed matcher 并不完全等价,尤其 code text 和 holiday。

4. 元数据示例

目录行声明
// ① 表级元数据在 owner 源码声明,Generator 编译期收集。
[BitzTable("SysLanguage", IsTenant = true, IsSoftDelete = true)]
[BitzIndex("UX_SysLanguage_Tenant_Code", "TenantId", "Code", IsUnique = true)]
public sealed class SysLanguageCatalogRecord : BizEntityBase
{
// ② BCP-47 code 是稳定业务键;显示名可以更新。
[BitzColumn(Length = 20, IsRequired = true)]
public string Code { get; set; } = string.Empty;
[BitzColumn(Length = 120)]
public string? DisplayName { get; set; }
}

示例裁剪了同一源码类型的非关键字段,完整定义以源码为准。

5. ORM 中立读取

MasterData 服务只注入 IEntitySet<T>:

字典行查询
// ① 表达式必须同时被当前支持的 ORM 翻译。
var entries = await codes.ListAsync(
row => row.Group == groupKey && row.IsActive && !row.IsDeleted,
cancellationToken);
// ② 排序在内存完成;大目录需评估是否改为可翻译排序端口。
var ordered = entries.OrderBy(row => row.Sort).ToList();
return ordered;

源码还显式写 !IsDeleted,即使表元数据已有软删过滤。这是防御性重复,而不是跨租户权限证明。

6. 三种“唯一”必须一致

一个可重复导入的目录至少涉及:

  1. 数据库 unique index;
  2. seed step WhereColumns/Match;
  3. 读取/缓存映射键。
否是

数据库唯一键

与 seed matcher 一致?

Seed Match

Resolver map key

覆盖错误 / 重复 / ToDictionary 异常

稳定目录语义

当前 SysGeneralCodeText 三者不一致:索引含 Class,seed matcher 只有 Code+Language,Resolver 也只按 Code 组图。

7. SysTranslation 与 I18n

MasterData 物理拥有 Translation/Language 行;I18n 拥有翻译应用契约、Repository、作用域覆盖和查询 API。依赖方向不是通过 MasterData Contracts 表达,因为 MasterData 没有 Contracts 项目;I18n Infrastructure 直接引用 MasterData Infrastructure。

这是一项已记录的物理耦合。未来若拆包独立交付,应提取窄的 persistence contracts/owner model 包,而不是复制表类型。

8. 目录模型不提供的规则

当前类型没有实现:

  • 国家/币种/行业标准版本和有效期;
  • 汇率来源、报价时间、精度与反向规则;
  • 节假日调休/工作日和发布机构;
  • 字典组 FK、树环与孤儿防护;
  • 默认语言唯一性;
  • 保护记录的写入阻断;
  • 乐观并发以外的发布审批和历史版本。

这些不是列属性可以自动补全的业务规则。

9. 新增目录的判断

新增数据前先问:

  • 是否跨多个模块共享且需要持久稳定 code?
  • 是否有权威来源、版本和更新责任人?
  • 是租户可覆盖,还是平台全局唯一?
  • 是否需要 I18n、有效期和历史回放?
  • 是否应属于某个业务模块而非 MasterData?

模块私有状态枚举通常留在归属模块;不要为“方便下拉框”就创建共享表。

10. 新行类型示例

新增参考目录检查骨架
// ① 使用 owner-local record,并明确租户和软删语义。
[BitzTable("SysExampleCatalog", IsTenant = true, IsSoftDelete = true)]
[BitzIndex("UX_SysExample_Tenant_Code", "TenantId", "Code", IsUnique = true)]
public sealed class SysExampleCatalogRecord : BizEntityBase
{
// ② 自然键必填,长度与权威标准一致。
[BitzColumn(Length = 32, IsRequired = true)]
public string Code { get; set; } = string.Empty;
[BitzColumn(Length = 160, IsRequired = true)]
public string DisplayName { get; set; } = string.Empty;
}

随后必须补 metadata gate、双 ORM parity、seed matcher、资产完整性和文档,而不仅是建类。

11. 测试证据

MasterDataInfrastructureArchitectureTests 固定:旧 Framework/Entity 路径删除、九行/八资产存在、程序集 ORM 中立、seed 基类、Host 组合、Resolver 的 IEntitySet 依赖和九种生成元数据。跨 ORM 集成测试注册这些类型,验证 adapter parity 与 seed 端到端。

这些测试没有证明目录业务规则、生产数据新鲜度或管理权限。

12. 核查命令

Terminal window
# 表与索引定义。
rg -n "BitzTable|BitzIndex|class .*CatalogRecord" src/Platform/MasterData -g '*.cs'
# 模块不应引用具体 ORM。
rg -n "SqlSugar|EntityFrameworkCore|Infrastructure.EfCore" src/Platform/MasterData -g '*.cs' -g '*.csproj'
# 架构门禁中的九行清单必须同步新增类型。
rg -n "Owner_Assembly_Should_Emit_Metadata" tests/BitzOrcas.Architecture.Tests/MasterDataInfrastructureArchitectureTests.cs

模块总览 · 种子资产

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%