VS Code Map失效,如何通过XML/XSLT将VB代码转换为DMGL有向图/代码映射
我无法让Visual Studio为孤立/离散的VB函数代码建模或生成代码映射。目前我正处理一个VB.Net编写的遗留应用,大部分应用代码与可执行文件对终端用户不透明,但表单背后支持用户自定义逻辑,可与表单字段、数据模型交互,我需要为这些用户自定义代码生成映射,排查循环逻辑等不良交互问题。
我的核心目标是生成实体关系图或依赖图,手动用Visio绘制全量代码的映射工作量过大,用Excel处理效率太低,正则表达式也不适用于这类场景。我尝试过VS code map,但它需要应用编译部分的引用,我没有相关资源,这些用户代码无类上下文,仅在运行时执行,属于VS无法识别的孤立代码:VS Code可对其做语法高亮,但Visual Studio及其附加工具code map、dmg(有向图)都无法识别,Roslyn也存在同样的适配问题,对象角色建模需要手动录入过于繁琐,用正则或Excel实现该需求相当于重复造轮子,成本过高。
我当前处理的是工时卡相关业务逻辑:父/头表单代表工时卡,对应Timecards表字段前缀为lmp*,子表单代表明细行,对应TimecardLines表字段前缀为lml*,函数间存在隐式调用、字段读写、外部依赖调用关系,纯文本形式难以可视化级联影响,我希望能将这些纯函数形式的用户代码转换为DGML格式,查看有限上下文下的函数相互引用关系,请问有没有可行的实现方案?
附部分函数接口示例
M1.Ax.Erp.Forms.Production.Timecard.TimecardLineView
- 父级为:M1.Ax.Erp.Forms.Production.TimecardView
- 说明:暂不考虑
_Delete()事件
Function lmlRoundedEndTime_ValueChanged(sender, e)
更新字段:
lmlLaborHours- 隐式调用:
lmlLaborHours_ValueChanged()
- 隐式调用:
lmlActualEndTime- 隐式调用:
TimecardLines_UpdateCompleted
- 隐式调用:
输入:
lmlRoundedStartTimelmlRoundedEndTimelmlShiftID
依赖:
App.Ax("Shift").CalculateHours()
行为设置:
xapLMUpdateActualWithRounded
隐式调用:
lmpRoundedEndTime_ValueChanged()
Function lmlRoundedStartTime_ValueChanged(sender, e)
更新字段:
lmlRoundedEndTime- 隐式调用:
lmlRoundedEndTime_ValueChanged
- 隐式调用:
TimecardLines_UpdateCompleted- 关联更新字段:
lmlLaborHours、lmlActualStartTime
- 关联更新字段:
输入:
lmlActualStartTimelmlRoundedStartTimelmlRoundedEndTimelmlLaborHourslmlShiftID
依赖:
App.Ax("Shift").CalculateEndTime()App.Ax("Shift").CalculateHours()
行为设置:
xapLMCalculateEndTimexapLMUpdateActualWithRounded
上述字段编辑后会更新父级头表单,可能触发多个父级函数产生不良交互:
M1.Ax.Erp.Forms.Production.Timecard.TimecardView
子级为:M1.Ax.Erp.Forms.Production.Timecard.TimecardLineView
以下函数可被TimecardLines_UpdateCompleted调用:
Function lmpRoundedStartTime_ValueChanged(sender, e)
更新字段:
lmpPayrollHours
输入:
lmpRoundedStartTimelmpRoundedEndTimelmpShiftID
依赖:
App.Ax("TimecardFunctions").CalculateHours()
行为设置:
xapDCPayCalculationMethod
Function lmpRoundedEndTime_ValueChanged(sender, e)
更新字段:
lmpPayrollHours
输入:
lmpRoundedStartTimelmpRoundedEndTimelmpShiftID
依赖:
App.Ax("TimecardFunctions").CalculateHours()
行为设置:
xapDCPayCalculationMethod
你不需要依赖VS的编译时分析能力,用「浅层语义提取+DGML模板生成」的轻量方案就能完全满足需求,整体实现成本很低,不需要重复造轮子:
1. 提取结构化关联关系
你提到的自定义代码结构非常规整,只有函数、字段、外部依赖三类节点,以及调用、读写、依赖三类关联,不需要做完整VB语法解析,用轻量的语法规则就能提取所有需要的信息:
- 用Python的
pyparsing库写30-50行匹配规则,批量扫描所有自定义代码文本,提取所有函数定义、更新的字段、隐式调用的函数、输入字段、外部依赖,输出三元组格式的关联数据(源节点、关系类型、目标节点)。 - 不需要处理完整的VB语法,只需要匹配你关心的
lml*/lmp*字段、*_ValueChanged/*_UpdateCompleted函数、App.Ax("*")外部依赖三类固定模式即可,比正则的适配性更高,不会出现误匹配。
2. 直接生成DGML文件
DGML是纯XML格式的有向图描述文件,结构非常简单,你只需要准备一个固定的DGML模板,把上一步提取到的节点和关联填入对应位置即可:
<!-- DGML模板示例 --> <DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml"> <Nodes> <!-- 这里填入提取到的所有节点,按类型加分类标签 --> <Node Id="lmlRoundedEndTime_ValueChanged" Label="lmlRoundedEndTime_ValueChanged" Category="Function" /> <Node Id="lmlLaborHours" Label="lmlLaborHours" Category="Field" /> <Node Id="App.Ax('Shift').CalculateHours" Label="Shift.CalculateHours" Category="External" /> </Nodes> <Links> <!-- 这里填入提取到的所有关联关系 --> <Link Source="lmlRoundedEndTime_ValueChanged" Target="lmlLaborHours" Label="更新字段" /> <Link Source="lmlRoundedEndTime_ValueChanged" Target="lmlLaborHours_ValueChanged" Label="隐式调用" /> </Links> <Categories> <!-- 不同类型节点加不同样式,方便可视化区分 --> <Category Id="Function" Background="LightBlue" /> <Category Id="Field" Background="LightGreen" /> <Category Id="External" Background="LightGray" /> </Categories> </DirectedGraph>
生成的DGML文件可以直接用Visual Studio打开,自动渲染为可交互的有向图,支持筛选节点、查找路径、放大缩小,完全满足你排查级联影响的需求。
3. 附加能力扩展
你需要的循环逻辑排查可以在生成DGML之前,对提取到的调用关系做一次简单的环路检测,直接输出有循环的函数调用链,不需要可视化后手动查找。如果后续自定义代码有更新,把整个流程封装成脚本,一键运行就能生成最新的依赖图,不需要手动维护。
内容的提问来源于stack exchange,提问作者Joseph Shirk

