驰骋BPM-ORM设计技术报告

驰骋 BPM ORM 设计 --- 技术报告

文档版本:2026-06

章节编号:1.1

依据代码:CCFlow/Components/BP.En30(核心 ORM 组件)

说明:本文基于源码静态分析撰写,用于技术分享与方案论证,力求准确描述设计思想并客观分析优劣。


一、缘起与设计目标

1.1 起始于 2002 年

驰骋 ORM 设计起始于 2002 年 ,起因是探索可复用的企业应用框架技术。当时需要一套能够:

  • 统一描述「数据表 ↔ 实体类」的映射关系;
  • 在多种数据库(SQL Server、Oracle、MySQL 等)上复用同一套业务代码;
  • 支撑快速搭建管理类应用(增删改查、列表查询、主从表编辑)。

EntityDBAccess.cs 文件头注释仍保留着创建时间标记:

1:5:CCFlow/Components/BP.En30/En/EntityDBAccess.cs 复制代码
/*
简介:负责存取数据的类
创建时间:2002-10
最后修改时间:2002-10
*/

1.2 设计目标(与市面 ORM 的差异点)

驰骋 ORM 从一开始就不是「纯数据访问层」,而是面向企业低代码应用的元数据框架:

维度 驰骋 ORM 目标 典型 ORM(EF Core / Hibernate)目标
核心对象 表 + 字段 + 控件 + 关系 类 + 属性 + 导航属性
元数据载体 Map / Attr / Attrs(代码 + 库表双轨) 特性标注 / Fluent API / 约定
产出物 CRUD + 表单 UI + 查询面板 + 主从/多对多 CRUD + LINQ 查询
运行方式 属性值存于 Row(Hashtable),弱类型读写 强类型属性 + 变更跟踪

这一取向决定了后续 CCFlow、JFlow、低代码表单、组织结构等模块均可在同一套元数据上叠加能力,而不必重复定义字段语义。


二、演进历程:XML → Map / Attr / Attrs

2.1 第一阶段:XML 描述映射(约 2002)

早期版本用 XML 文件 表达数据表与实体类的完整关系。Map.GenerMap(string xml) 方法至今仍保留这一能力:读取 XML 中的 Base、Attr、SearchAttr、Dtl、Dot2Dot 等节点,动态构建 Map 对象。

743:773:CCFlow/Components/BP.En30/En/MapExt/Map.cs 复制代码
        #region  生成属性根据xml.
        private string PKs = "";
        public void GenerMap(string xml)
        {
            DataSet ds = new DataSet("");
            ds.ReadXml(xml);
            foreach (DataTable dt in ds.Tables)
            {
                switch (dt.TableName)
                {
                    case "Base":
                        this.DealDT_Base(dt);
                        break;
                    case "Attr":
                        this.DealDT_Attr(dt);
                        break;
                    case "SearchAttr":
                        this.DealDT_SearchAttr(dt);
                        break;
                    case "Dtl":
                        this.DealDT_SearchAttr(dt);
                        break;
                    case "Dot2Dot":
                        this.DealDT_Dot2Dot(dt);
                        break;
                    default:
                        throw new Exception("XML 配置信息错误,没有约定的标记:" + dt.TableName);
                }
            }
            // 检查配置的完整性。
        }

XML 方案的优点是映射与代码解耦、便于工具生成;缺点是调试困难、版本管理成本高、缺乏编译期检查。

2.2 第二阶段:Map / Attr / Attrs 类(约 2003)

2003 年 ,架构改造为以 Map、Attr、Attrs 三个核心类在 C# 代码中描述映射,成为至今沿用的主路径。

类 职责
Map 描述一个实体(En)的完整元数据:物理表名、主键策略、字段集合、从表、多对多、查询条件、关联方法等
Attr 描述单个字段/属性:键名、物理列名、数据类型、字段逻辑类型、UI 控件、可见性、默认值、外键绑定等
Attrs Attr 的集合,提供 AddTBString、AddTBInt、AddDDLEntities、AddDDLSysEnum 等工厂方法,简化 Map 构建

实体子类通过重写 EnMap 属性返回自己的 Map 定义。以组织结构中的 Emp(用户)为例:

346:382:CCFlow/Components/BP.En30/Port/Emp.cs 复制代码
        public override Map EnMap
        {
            get
            {
                if (this._enMap != null)
                    return this._enMap;
                Map map = new Map("Port_Emp", "用户");

                #region 基本属性
                map.EnDBUrl = new DBUrl(DBUrlType.AppCenterDSN);
                map.IndexField = EmpAttr.FK_Dept;
                #endregion

                #region 字段
                map.AddTBStringPK(EmpAttr.No, null, "编号", true, false, 1, 50, 30);
                // ...
                map.AddTBString(EmpAttr.Name, null, "名称", true, false, 0, 200, 30);
                map.AddDDLEntities(EmpAttr.FK_Dept, null, "部门", new BP.Port.Depts(), false);
                // ...
                #endregion 字段

代码化 Map 带来了编译期类型安全(字段常量类如 EmpAttr)、IDE 跳转、与业务逻辑同仓版本管理等优势,XML 路径退居兼容与工具导入用途。


三、核心架构

3.1 类层次与职责

classDiagram class EnObj { +Row _row +Map _enMap +GetValByKey() +SetValByKey() } class Entity { +Insert() +Update() +Delete() +Retrieve() +CheckPhysicsTable() } class EntityNoName { PK = &#34;No&#34; } class EntityOID { PK = &#34;OID&#34; } class EntityMyPK { PK = &#34;MyPK&#34; } class Entities { +RetrieveAll() +RetrieveByAttr() } class Map { +Attrs +Dtls +AttrsOfOneVSM +AddTBString() +AddDtl() } class Attr { +Key Field +MyFieldType +UIContralType } EnObj <|-- Entity Entity <|-- EntityNoName Entity <|-- EntityOID Entity <|-- EntityMyPK Entity --> Map : EnMap Map --> Attrs Attrs --> Attr Entities --> Entity : 集合

目录结构(BP.En30):

目录/文件 说明
En/ 实体基类、Map/Attr/Attrs、SqlBuilder、QueryObject、EntityDBAccess
DA/ 数据库访问、DBAccess、DataType、缓存
Sys/ 低代码表单元数据:MapAttr、MapData、MapDtl、SFTable、SFDBSrc
Port/ 组织结构实体(Emp、Dept、Station 等),均基于本 ORM 定义

3.2 数据承载:Row 弱类型模型

实体字段值不直接映射为 C# 自动属性,而是存放在 Row(继承 Hashtable) 中,通过 GetValByKey / SetValByKey 读写:

13:42:CCFlow/Components/BP.En30/En/Row.cs 复制代码
    public class Row : Hashtable
    {
        // ...
        public void LoadAttrs(Attrs attrs)
        {
            this.Clear();
            foreach (Attr attr in attrs)
            {
                switch (attr.MyDataType)
                {
                    case DataType.AppInt:
                        // ...

设计意图:

  • 低代码场景下字段可运行时增删,无需重新编译实体类;
  • 主表、从表、流程表单共用同一套读写 API;
  • 业务类仍可封装强类型属性(如 Emp.Name),内部转调 GetValStringByKey。

3.3 持久化流水线

flowchart LR A[Entity.EnMap] --> B[SqlBuilder] B --> C[SQL + Paras] C --> D[EntityDBAccess] D --> E[DBAccess / SFDBSrc] E --> F[(数据库)] F --> G[fullDate 填充 Row]
组件 职责
SqlBuilder 根据 Map/Attrs 生成 SELECT/INSERT/UPDATE/DELETE,处理多主键、多数据库方言
SQLCache 缓存实体级 SQL 模板,减少重复拼接
EntityDBAccess 执行 SQL、异常时触发 CheckPhysicsTable 自动建表/扩列
QueryObject 链式构造 WHERE/ORDER BY,供 Entities 批量查询

3.4 主键策略

通过实体基类约定主键字段名:

基类 主键 典型场景
EntityNoName No(字符串) 部门、用户、角色、流程编号
EntityOID OID(自增整型) 运行实例、从表明细
EntityMyPK MyPK(组合字符串) DeptEmp、DeptEmpStation 等关联表

FieldType 枚举进一步区分字段逻辑角色:普通、主键、外键、枚举、主键+外键、关联文本、虚拟字段等。


四、超越字段映射:BP 扩展能力

原始 ORM 仅解决「数据库列 ↔ 实体属性」映射;驰骋 BP 层在此基础上扩展为应用元数据框架。

4.1 字段逻辑类型(FieldType / FieldTypeS)

141:197:CCFlow/Components/BP.En30/En/EnumLab.cs 复制代码
    public enum FieldTypeS
    {
        Normal = 0,   // 普通属性字段
        Enum = 1,     // 枚举字段
        FK = 2        // 外键字段
    }
    public enum FieldType
    {
        Normal, PK, FK, Enum, PKFK, PKEnum, RefText, NormalVirtual, MultiValues
    }
类型 含义 典型 API
属性字段 普通列,文本框/数值/日期 AddTBString、AddTBInt、AddTBDate
枚举字段 绑定 Sys_Enum 或字符串枚举 AddDDLSysEnum
外键字段 绑定另一 Entities 集合 AddDDLEntities
外部数据源字段 绑定 SFTable(SQL/WS/Handler 等) UIBindKey → SFTable
关联文本 外键旁的冗余显示列(如 FK_Dept + FK_DeptT) 自动追加 key + "T" 的 Attr

Attr.ToMapAttr 与 MapAttr.HisAttr 构成代码 Map ↔ 库表 MapAttr 双向转换桥梁,使设计态(设计器保存到 Sys_MapAttr)与运行态(GenerMap() 动态生成 Map)保持一致。

4.2 控件类型映射(UIContralType)

Attr 不仅记录 MyDataType(存储类型),还记录 UIContralType(呈现控件):

  • 基础:TB(文本框)、DDL(下拉)、CheckBok、RadioBtn
  • 流程/公文:HandWriting(签名)、FrmImgAth(图片附件)、GovDocFile(正文)、JobSchedule(进度图)
  • 移动端:MapPin、MobilePhoto、Location

低代码表单设计器将控件配置写入 Sys_MapAttr,运行时 MapDtl.GenerMap() 遍历 MapAttrs 调用 map.AddAttr(mapAttr.HisAttr) 合成完整 Map,从而一份元数据同时驱动持久化与 UI 渲染。

4.3 主表---从表映射(EnDtl)

Map.AddDtl 声明主实体下的从表集合:

516:525:CCFlow/Components/BP.En30/En/MapExt/Map.cs 复制代码
        public void AddDtl(Entities ens, string refKey, string groupName = null, DtlEditerModel model = DtlEditerModel.DtlBatch, string icon = null)
        {
            EnDtl dtl = new EnDtl();
            dtl.Ens = ens;
            dtl.RefKey = refKey;
            dtl.GroupName = this.currGroupMethodName;
            dtl.DtlEditerModel = model;
            dtl.Icon = icon;
            this.Dtls.Add(dtl);
        }
  • refKey:从表中外键列(如 FK_Node);
  • DtlEditerModel:批量编辑、查询编辑、自定义 URL 等模式;
  • 流程表单明细(MapDtl)、组织结构子面板均基于此机制。

4.4 一对多 / 多对多映射(AttrOfOneVSM)

Map.AttrsOfOneVSM 描述「一」端实体与「多」端实体通过中间表(MM 表)的关联,例如用户维护所属部门:

405:407:CCFlow/Components/BP.En30/Port/Emp.cs 复制代码
                map.AttrsOfOneVSM.AddBranches(new DeptEmps(), new BP.Port.Depts(),
                   BP.Port.DeptEmpAttr.FK_Emp,
                   BP.Port.DeptEmpAttr.FK_Dept, "部门维护", EmpAttr.Name, EmpAttr.No, "@WebUser.DeptNo");

支持树形部门、平铺面板等多种维护 UI(Dot2DotModel),直接生成部门-人员维护界面,无需手写关联 CRUD。

4.5 外部数据源(SFTable / SFDBSrc)

  • SFTable:定义下拉/列表的外部数据来源(本库 SQL、跨库、WebService、Java 处理器等);
  • SFDBSrc :多数据源连接配置,使 Map 可指定 DBSrc 指向非本地库;
  • 字段通过 UIBindKey 关联 SFTable,实现不建物理外键列也能绑定外部数据的下拉选项。

4.6 自动 DDL(CheckPhysicsTable)

实体在 Insert/Update 失败时可触发 CheckPhysicsTable():按 Map.Attrs 定义自动创建表或扩展列长。这使低代码「先画表单、后建表」成为可能,也是与「迁移脚本驱动」的 ORM 的显著差异。

4.7 查询与权限扩展

Map 还承载:

  • SearchNormals / SearchFKEnums:列表页查询条件;
  • HisRefMethods:实体关联功能(修改密码、部门角色维护等);
  • UAC:插入/删除/更新/查看权限;
  • AddHidden:数据范围过滤(如 SAAS 模式 OrgNo = @WebUser.OrgNo)。

五、生态承载:基于 ORM 构建的产品模块

驰骋 ORM 不是孤立的数据层,而是 CCFlow / JFlow / 驰骋 BPM 低代码平台 的统一元数据底座。

flowchart TB ORM[&#34;BP.En30 ORM<br/>Map / Attr / Entity / Entities&#34;] ORM --> WF[&#34;CCFlow / JFlow 工作流<br/>节点、方向、表单绑定&#34;] ORM --> Form[&#34;低代码表单<br/>MapData / MapAttr / MapDtl&#34;] ORM --> Port[&#34;组织结构<br/>Port_Emp / Port_Dept / Port_Station&#34;] ORM --> Sys[&#34;系统管理<br/>枚举、权限、数据源&#34;] WF --> Runtime[&#34;流程运行:GEEntity / GEDtl 通用实体&#34;] Form --> Runtime Port --> Worker[&#34;选人引擎:部门+角色 SQL&#34;]
模块 与 ORM 的关系
驰骋 BPM / CCFlow 流程模板、节点、表单均用 Entity/Entities 定义;运行期表单数据通过 GEEntity(通用实体)+ 动态 GenerMap() 读写
JFlow Java 版移植同一套 Map/Attr 语义,保持跨语言元数据一致
低代码 设计器操作 Sys_MapData/Sys_MapAttr;运行时合成 Map;主从表、外键、枚举、外部数据源均落在 Attr 扩展属性上
组织结构 Port_* 五表实体均在 BP.Port 用 EnMap 声明;DeptEmp、DeptEmpStation 用 EntityMyPK;人员界面用 AttrsOfOneVSM 维护多部门
权限 / 报表 / 接口 统一 GetValByKey 读写;QueryObject 构造列表;ToJsonObject() 输出 API

结论:更换 ORM 意味着重写元数据模型、表单设计器、流程引擎数据层、组织结构集成接口------成本极高,因此该 ORM 是平台级基础设施而非可插拔组件。


六、与市面 ORM 的对比评估

6.1 对比总表

维度 驰骋 ORM(BP.En30) Entity Framework Core Hibernate / JPA Dapper
定位 元数据 + ORM + UI 描述 标准 .NET ORM Java 标准 ORM 微 ORM / SQL 映射
映射定义 Map/Attr 代码 + Sys_MapAttr 库表 特性 / Fluent API 注解 / XML 手写 SQL
查询方式 QueryObject + SqlBuilder LINQ HQL / Criteria / JPQL 原始 SQL
变更跟踪 无(显式 Update) 有 有(Session) 无
UI 元数据 内置(控件、布局、从表) 无 无 无
主从 / 多对多 EnDtl / AttrOfOneVSM 一等公民 导航属性配置 关联映射 自行实现
自动建表 CheckPhysicsTable Migrations hbm2ddl 无
多数据库 SqlBuilder 方言分支 提供商插件 方言 取决于 SQL
低代码友好 极高 低 低 低
强类型开发体验 中(Row + 部分封装属性) 高 高 高
学习曲线(纯 CRUD) 中(需理解 Map 体系) 中 中高 低

6.2 驰骋 ORM 的优势

  1. 元数据一体 :同一 Attr 描述列类型、控件、外键、枚举、默认值,设计器与运行引擎零阻抗。
  2. 企业功能内建:主从表、多对多维护、列表查询、权限位、关联方法,减少重复劳动。
  3. 低代码原生 :运行期可 GenerMap(),字段可来自数据库配置,适合用户自定义表单。
  4. 久经生产验证:2002 年至今,与 CCFlow 流程场景深度耦合(附件、签名、公文、轨迹等控件类型)。
  5. 多数据库与国产库 :EntityDBAccess 内可见 Oracle、MySQL、PostgreSQL、达梦、人大金仓等分支。

6.3 驰骋 ORM 的局限

  1. 非标准 ORM:无法直接用于普通 .NET 项目的「引包即用」,与 EF Core 生态(LINQ、迁移、工具链)不兼容。
  2. 弱类型 Row:大型团队纯代码开发时,缺少编译期字段检查,重构依赖全局搜索。
  3. 无现代查询抽象 :没有 LINQ / IQueryable,复杂查询依赖 SqlBuilder 字符串,可读性与组合性弱于 EF。
  4. 无变更跟踪 :更新需显式调用 Update() 并指定字段,易遗漏或误更新。
  5. XML 遗产 :GenerMap(xml) 与代码 EnMap 双路径并存,增加维护心智负担。
  6. 测试与 Mock :Entity 与 DBAccess 静态耦合紧密,单元测试需依赖数据库或大量封装。

6.4 适用场景判断

场景 建议
驰骋 BPM / CCFlow / JFlow 二次开发 必须使用本 ORM,无替代
低代码表单 + 流程 + 组织结构一体部署 最适合
全新 .NET 业务系统、与 BPM 无关 不推荐,应选 EF Core + 独立 DTO
仅需高性能只读查询 可在 Entities 层旁路 DBAccess.RunSQL,但失去元数据收益
与外部 ORM 共存 仅建议边界清晰的数据同步,避免同一表双映射

七、典型开发模式示例

7.1 声明式实体(代码优先)

csharp 复制代码
// 1. 字段常量类
public class XxxAttr { public const string Title = "Title"; }

// 2. 实体类
public class Xxx : EntityNoName
{
    public override Map EnMap
    {
        get
        {
            if (_enMap != null) return _enMap;
            Map map = new Map("My_Xxx", "示例");
            map.AddTBStringPK(XxxAttr.No, null, "编号", true, false, 1, 50, 20);
            map.AddTBString(XxxAttr.Title, null, "标题", true, false, 0, 200, 30);
            map.AddDDLSysEnum("Status", 0, "状态", true, false, "XxxStatus");
            _enMap = map;
            return _enMap;
        }
    }
}

// 3. 集合类
public class Xxxs : EntitiesNoName
{
    public override Entity GetNewEntity => new Xxx();
}

7.2 运行时使用

csharp 复制代码
Xxx en = new Xxx();
en.No = "001";
en.SetValByKey(XxxAttr.Title, "测试");
en.Insert();

Xxxs ens = new Xxxs();
ens.RetrieveByAttr("Status", 1);

Xxx en2 = new Xxx("001");
en2.RetrieveFromDBSources();
string title = en2.GetValStringByKey(XxxAttr.Title);

7.3 低代码动态表单(元数据优先)

  1. 设计器保存字段 → Sys_MapAttr;
  2. MapDtl.GenerMap() / MapData.GenerMap() 合成 Map;
  3. GEDtl / GEEntity 作为通用实体承载数据;
  4. 前端根据 MapAttr JSON 渲染控件。

八、总结

驰骋 ORM 是一段从 2002 年 XML 映射 演进到 2003 年 Map/Attr/Attrs 代码化元数据 、并在二十余年中持续扩展为低代码应用框架底座的技术路线。

它的本质不是「对象---关系映射库」,而是:

用统一的 Map 元数据,同时描述数据库结构、实体行为、UI 控件、主从与多对多关系、外部数据源与查询条件,并驱动 CCFlow、JFlow、组织结构与低代码表单的全生命周期。

与 Entity Framework、Hibernate 等市面 ORM 相比,驰骋 ORM 在标准性、强类型、现代查询 上不占优,但在低代码、流程、表单、组织一体化场景具有不可替代的语义内聚优势。理解这一边界,是正确使用和扩展驰骋 BPM 平台的前提。


附录:核心类型索引

类型 路径 说明
Map En/MapExt/Map.cs 实体元数据根对象
Attr / Attrs En/MapExt/Attr.cs 字段元数据与集合
Entity / Entities En/Entity.cs、En/Entities.cs 实体与集合基类
Row En/Row.cs 字段值容器
SqlBuilder En/SqlBuilder.cs SQL 生成
EntityDBAccess En/EntityDBAccess.cs 持久化执行
QueryObject En/QueryObject.cs 查询构造器
EnDtl / AttrOfOneVSM En/EnDtl.cs、En/MapExt/AttrOfOneVSM.cs 从表与多对多
MapAttr Sys/MapAttr.cs 低代码字段元数据(库表)
MapData / MapDtl Sys/MapData.cs、Sys/MapDtl.cs 表单/从表定义
SFTable / SFDBSrc Sys/SFTable.cs、Sys/SFDBSrc.cs 外部数据源
相关推荐
愚农搬码10 天前
Flowable 工作流中如何接入大模型 LLM 节点
llm·ai编程·工作流引擎
愚农搬码17 天前
Flowable工作流引擎如何适配国产数据库?以达梦数据库举例说明
工作流引擎
songgeb17 天前
mspec体验:基于SDD的轻量AI工作流
ai编程·工作流引擎
大龄码农有梦想17 天前
企业工作流系统如何设计用户、部门、角色、岗位、动态关系五类流程办理人?
工作流引擎·flowable·流程引擎·oa·工作流系统·选人规则·bpm平台
愚农搬码20 天前
工作流里的转办、委托和工作移交的业务语义与技术实现
工作流引擎
大龄码农有梦想20 天前
工作流中的子流程节点能否驳回到父流程?
工作流引擎·流程引擎·oa·bpm·子流程·驳回·退回
愚农搬码20 天前
工作流中的子流程节点能否驳回到父流程?
工作流引擎
愚农搬码20 天前
流程图上的回退线与运行时动态回退,应该选择哪一种?
工作流引擎
大龄码农有梦想21 天前
开源流程引擎 Camunda 如何实现任意节点跳转?
工作流引擎·流程引擎·camunda·oa·bpn·流程跳转·会签
愚农搬码24 天前
在开源流程引擎 Flowable 里如何实现任意节点跳转?
工作流引擎