DncZeus实战开源项目(四)数据库设计
// dbContext.cs
......
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DncPermission>(entity =>
{
// 设置权限Code为唯一索引
entity.HasIndex(x => x.Code).IsUnique();
/*
物理层面:Permission (1) ←→ (1) Menu (一对一记录)
业务层面:Menu (1) ←→ (*) Permission (一对多)
*/
entity.HasOne(x => x.Menu).WithMany(x => x.Permissions).HasForeignKey(x => x.MenuGuid);
});
base.OnModelCreating(modelBuilder);
}
表关系的正确理解
1. 物理表关系:一对一
从数据库表层面看,这确实是一对一关系:
DncPermission 表 DncMenu 表
----------------- ------------
PermissionId (PK) MenuId (PK)
MenuGuid (FK) ←→ Id (PK)
每个权限记录只能属于一个菜单(通过外键 MenuGuid),这就是物理表的一对一关系。
2. 业务逻辑关系:一对多
从业务对象层面看,这是一对多关系:
- 一个菜单 可以有多个权限
- 一个权限 只能属于一个菜单
3. 混淆的根源:来自于两个不同层面的视角
错误理解:
- 认为表关系应该是"一对多"
- 但实际上每个权限记录只能关联一个菜单记录
正确理解:
业务层面:Menu (1) ←→ (*) Permission (一对多)
物理层面:Permission (1) ←→ (1) Menu (一对一记录)
4. 具体示例说明
数据示例:
-- DncMenu 表
MenuId MenuName
1 用户管理
2 系统设置
-- DncPermission 表
PermissionId PermissionCode MenuGuid
101 user.view 1
102 user.edit 1
103 system.config 2
关系解释:
- 菜单"用户管理"(ID=1) 有两个权限:查看、编辑
- 每个权限记录只能指向一个菜单记录
- 这就是一对多关系通过外键实现一对一物理连接
5. EF Core 代码映射
// 在 Menu 实体中
public class DncMenu
{
public Guid Id { get; set; }
public string Name { get; set; }
// 一个菜单有多个权限
public ICollection<DncPermission> Permissions { get; set; }
}
// 在 Permission 实体中
public class DncPermission
{
public Guid Id { get; set; }
public string Code { get; set; }
// 一个权限属于一个菜单
public Guid MenuGuid { get; set; }
public DncMenu Menu { get; set; }
}
6. 总结
不会造成混乱的原因:
- 物理层面:每条权限记录只关联一条菜单记录(一对一)
- 逻辑层面:从菜单角度可以找到多个权限(一对多)
- 外键约束:保证了数据完整性
这是关系数据库中标准的一对多关系实现方式,通过在"多"的一方添加外键指向"一"的一方来实现。
我来为你画一个 Mermaid 图来理清物理关系和业务逻辑关系:
erDiagram
%% 业务逻辑关系(概念层)
DncMenu {
Guid Id "主键"
string Name "菜单名称"
string Icon "图标"
int Sort "排序"
}
DncPermission {
Guid Id "主键"
string Code "权限代码"
string Name "权限名称"
Guid MenuGuid "外键"
}
%% 物理表关系(数据层)
%% 一对多关系通过外键实现
DncMenu ||--o{ DncPermission : "1对多(业务层)"
%% 每个权限记录只能指向一个菜单记录
DncPermission }o--|| DncMenu : "1对1(物理层)"
classDiagram
%% 业务对象模型
class DncMenu {
+Guid Id
+string Name
+string Icon
+int Sort
+ICollection~DncPermission~ Permissions
+GetPermissions() List~DncPermission~
}
class DncPermission {
+Guid Id
+string Code
+string Name
+Guid MenuGuid
+DncMenu Menu
+GetMenu() DncMenu
}
%% 业务关系:一对多
DncMenu "1" *-- "*" DncPermission : 业务逻辑关系
note for DncMenu "一个菜单可以有多个权限"
note for DncPermission "一个权限只能属于一个菜单"
graph LR
subgraph "物理数据库层面"
M1[菜单表记录1<br/>用户管理]
M2[菜单表记录2<br/>系统设置]
P1[权限记录1<br/>user.view]
P2[权限记录2<br/>user.edit]
P3[权限记录3<br/>system.config]
M1 --> P1
M1 --> P2
M2 --> P3
end
subgraph "业务逻辑层面"
UserMenu[用户管理菜单]
SystemMenu[系统设置菜单]
UserMenu --> |包含| ViewPerm[查看权限]
UserMenu --> |包含| EditPerm[编辑权限]
SystemMenu --> |包含| ConfigPerm[配置权限]
end
style M1 fill:#e1f5fe
style M2 fill:#e1f5fe
style P1 fill:#f3e5f5
style P2 fill:#f3e5f5
style P3 fill:#f3e5f5
3. 关系总结
- 数据库设计:在权限表中添加外键指向菜单表(标准的一对多实现)
- 查询方式:从菜单找权限是"一对多",从权限找菜单是"一对一"
- 本质:这是关系数据库中标准的一对多关系模式,没有混乱

浙公网安备 33010602011771号