Skip to content

一表多页面(视图模式) ​

场景:一张 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(数据层共享是需求本身)

五、推进建议 ​

  1. 先拿一个视图(如原材料)走通全流程,趟平三个坑
  2. 复制模式到其余视图,边际成本极低(建视图 → 生成 → Service 套模板 → 配菜单)
  3. 新表要一表多页面时,直接按本文模板套

参考 ​