外观
一表多页面(视图模式)
场景:一张
Data_Item(物料)表,要做成原材料管理、辅料管理、产品管理等多个页面——每个页面字段部分重叠、叫法不同、权限各自独立。这是 ERP"物料主数据多视角"的经典需求,VOL 官方推荐做法就是数据库视图。
一、为什么走视图模式
先看生成器的一个硬约束(源码实证):生成器配置按表名唯一加载,Sys_TableInfoService.cs 三处均为:
csharp
repository.FindAsIQueryable(x => x.TableName == tableName)
.Includes(o => o.TableColumns).ToList().FirstOrDefault();同一张表在生成器里永远只有一套列配置——三个页面三套叫法没法在生成器里配三次。
两条路线对比:
| 路线 A:一套后端 + 3 个前端页面 | 路线 B:视图 ×3(本文) | |
|---|---|---|
| 字段/叫法独立 | ✅ 页面层各自改 | ✅ 生成器配置层独立 |
| 按钮/菜单权限独立 | ❌ 共用接口,权限一体 | ✅ 三条菜单各自授权 |
| 保存逻辑 | 一处 | 每个视图 Service 重写 3 个方法(模板化) |
| 适用 | 权限不分家的简单复用 | 权限要分、各视角持续演化 |
权限要分页控制、未来各视角会长出专属字段/按钮 → 选视图模式。
二、完整步骤(5 步)
第 1 步:数据库建视图(每页面一个)
sql
CREATE VIEW V_RawMaterial AS
SELECT ItemId, ItemCode, ItemName, Spec, Unit, Price, StockQty -- 只挑该页需要的字段
FROM Data_Item
WHERE ItemCategory = N'原材料';
-- V_AuxiliaryItem / V_Product 同理要点:
- 明确列清单,别用
SELECT *——基表以后加字段不会自动进视图,行为可控 - 每个视图 = 该页面的"字段白名单 + 数据范围"
- 保持物理列名(原因见第三节坑 1)
第 2 步:生成器对每个视图生成完整代码
- 代码生成器表名直接填视图名(框架把视图当表)
- 手动指定主键(如
ItemId)——视图没有主键,必须配置;自增标识按基表实际填 - 字段标签在此配置("原料名称"/"品名"),多个视图的配置互不影响
- 生成实体 / Service / Controller / 前端页面全套
第 3 步:视图 Service 重写增删改(官方标准做法)
官方文档明确:视图不能直接增删改,不要调 base.Add/Update/Del。在每个视图 Service 里重写三个方法,委托给原表 Service:
csharp
public partial class V_RawMaterialService
{
public override WebResponseContent Add(SaveModel saveModel)
{
saveModel.MainData["ItemCategory"] = "原材料"; // 视图没有的列,新增时必须补
return Data_ItemService.Instance.Add(saveModel);
}
public override WebResponseContent Update(SaveModel saveModel)
=> Data_ItemService.Instance.Update(saveModel);
public override WebResponseContent Del(object[] keys, bool autoDelDetail = true)
=> Data_ItemService.Instance.Del(keys, autoDelDetail);
}查询一行都不用改——列表/条件查询走视图,自动只见本类别数据。
第 4 步:配置菜单与权限(收益所在)
- 三个视图 = 三条独立菜单
Sys_Menu.Auth各自定义按钮集(原材料页可以不配删除按钮)Sys_RoleAuth各角色勾选
"产品页可删除、原料页只读"只是菜单按钮配置差异,零代码。权限判定链路:Sys_Menu.Auth(定义)→ Sys_RoleAuth.AuthValue(勾选)→ UserContext 聚合判定。
第 5 步:验证清单(每个视图页面过一遍)
- [ ] 列表正常显示、只见本类别数据
- [ ] 条件查询正常
- [ ] 新增:字段全落库,类别字段正确(坑 2)
- [ ] 编辑:修改落库,主键传递正确
- [ ] 删除正常(若该页配了删除按钮)
- [ ] 导出/打印正常
- [ ] 权限:无按钮权限的页面按钮隐藏、接口 403
三、三个关键坑
坑 1:叫法不同 ≠ 列名不同
别在视图里改列名(SELECT ItemName AS 原料名称 ❌)。叫法差异在前端 columns 的 title 改(页面/extension 层),视图保持物理列名——否则 saveModel 字段名与原表对不上,保存要做字段映射(官方提到"字段不一致要先改 saveModel 再传原表 Service"),纯属自找麻烦。
坑 2:新增报错或类别字段为空
视图里没有的列(如 ItemCategory),新增时 Add 重写里手动补,见第 3 步代码。漏补会导致原表插入 null 或直接报错。
坑 3:视图用 SELECT *
基表加字段后视图行为不可控(部分数据库视图仍返回旧结构),且生成器读到的列结构可能过时。始终显式列清单。
四、原理速查
| 问题 | 答案 |
|---|---|
| 为什么查询不用改代码 | 列表查询走视图,WHERE 过滤已内建在视图里 |
| 为什么增删改要委托原表 | 视图不可写(官方 viewtable 章节),调 base.Xxx 会直接失败 |
| 为什么权限天然独立 | 每个视图是独立的"表名",Sys_Menu/Sys_RoleAuth/接口权限过滤全按表名隔离 |
| 三个页面会互相干扰吗 | 不会,除了物理上写同一张 Data_Item(数据层共享是需求本身) |
五、推进建议
- 先拿一个视图(如原材料)走通全流程,趟平三个坑
- 复制模式到其余视图,边际成本极低(建视图 → 生成 → Service 套模板 → 配菜单)
- 新表要一表多页面时,直接按本文模板套
参考
- 官方文档:视图新增、删除、修改、多表关联视图生成代码
- 本站相关:VOL 框架表结构说明(权限三表关系)、后端源码分析(BaseProvider/钩子体系)