使用 PowerBuilder .NET 12.1 EBF 进行项目分区来切分“大饼”
在本文中,我将介绍 21.1 EBF 3 月发布版中包含的新项目绘制器(Project Painter)功能,该功能让您能够控制部署的架构方式。
各种各样的“大饼”都有,比如鸡肉派、牧羊人派、樱桃派,当然还有经典的美国苹果派。每一位主人都知道,派都是完美的圆形。有无数种方法可以分割和享用它们。它们可以被切成两半、三份、四份、八份、十六份,或者任何这些的组合。如果派很小,您甚至可以选择为每位客人提供一个完整的派。由您决定哪种大小的份量适合您的客人。
从某种意义上说,配置 .NET 应用程序以进行部署就像分割一个派一样。您可以将整个应用程序作为一个整体(单个 EXE)进行部署,也可以将其分解为一组相关的较小程序集。如何将应用程序划分为单独的部署单元,确实是您的选择。
在本文中,我将介绍 21.1 EBF 3 月发布版中包含的新项目绘制器(Project Painter)功能,该功能让您能够控制部署的架构方式。我将向您介绍这项功能为何对您很重要,并展示如何使用配置工具来实现您想要的结果。
背景
我们都知道,许多 PowerBuilder 的业务线应用程序都很庞大。事实上,有些甚至可以说是巨型。应用程序的源代码可能达到数百兆字节。尽管尺寸巨大,但在传统的 Win32 环境中,开发人员可以做一个小的改动,点击“运行器”图标,然后立即启动并测试应用程序,而无需承担编译阶段的时间成本。这种开发方法是 PowerBuilder 光辉名片上列出的伟大功能之一。这种方法得到了 PBL 结构的支撑,该结构除了源代码版本外,还包含每个代码对象的编译版本。测试总是快速而方便。
此外,作为一名传统的开发人员,您可以选择部署架构。您可以将所有代码构建到一个单独的 EXE 中,或者选择 EXE 与一组相关的 PBD 或 DLL 的组合。除了缩短构建时间,选择 PBD/DLL 方案还能在部署更改方面为您提供灵活性。
.NET 的区别
.NET 是一个不同的平台。.NET 中不存在即时运行(Instant Run)功能。二进制代码不再与 PBLX 中的源代码一起存储。每一次更改,无论多小,都需要在测试前经过编译阶段。即使您可以(并且将会)启动增量编译以最大限度地缩短编译时间,默认情况下所有代码都会被构建到一个单独的大型 EXE 中。对于大型应用程序,编译阶段可能是一个耗时的过程,会严重影响开发人员的生产力。(想象一下,改了一行代码,开始编译,开车去咖啡店然后回来,编译还没完成?)
将代码编译成单个 EXE 的另一个潜在的致命影响发生在编译非常大的应用程序时。编译器可能会耗尽内存并崩溃(ABEND)!作为一名良好的 32 位应用程序,PowerBuilder WPF .NET 及其合作伙伴 Microsoft C# 编译器本身只支持 2GB 的虚拟内存。即使将 .NET Framework 可执行文件设置为“大地址感知”(large address aware)之后,编译器在处理大型应用程序时仍有可能耗尽内存。(请参阅 PowerBuilder 12.0 文档部分 > PowerBuilder .NET 功能指南 > 关于 PowerBuilder .NET >“大型应用程序的内存调优”。有关更多背景信息,请在互联网上搜索“.NET Framework large address aware”)。对于 12.0 GA 和 12.1 版本中的这些大型应用程序,唯一的选择是手动将它们拆分成可独立部署的 DLL。然而,手动分区是一个痛苦而耗时的过程;我推测对于大多数公司来说,这都不是一个可行的方案。
一个解决方案
2011 年 3 月的 EBF 是一个基础功能发布。它在三个主要领域增强了核心功能。其中一个领域是应用程序部署。在本文的其余部分,我将重点介绍此功能。您将了解如何使用工具和技术来克服阻碍 .NET 迁移的部署问题,甚至可能能够恢复 RAD 开发在 PowerBuilder.NET 部署中的应有地位。
程序集配置工具
在本节中,我将介绍一些您将用于配置部署架构的新工具。
- 代码依赖性检查器 (The code dependency checker):它的作用是构建程序集引用树。它会检查并报告循环引用。在图 1 中,您可以看到当项目绘制器(Project Painter)打开时,可以从“设计”(Design)菜单访问此工具,在图 2 中,您可以在解决方案资源管理器(Solution Explorer)中的项目对象(Project object)上下文菜单中找到它。图 1:检查依赖项菜单项图 2:项目对象上下文菜单
- 程序集映射视图 (The assembly mapping view):使用此项目绘制器视图(如图 3 所示)上的编辑字段,来指定您的 PBLX 到程序集的映射选择。该视图作为选项卡添加到项目绘制器中。您的选择以 XML 格式存储在项目文件(srj)中。(如图 4 所示)。图 3:程序集映射视图图 4:srj 项目文件中的映射
- 依赖项显示选项卡 (Dependencies Display Tab):项目绘制器(Project Painter)中的一个单独选项卡(如图 5 所示),包含一个树状视图,在检查依赖项后会被填充。程序集按编译依赖顺序排列。图 5:依赖项显示选项卡
- 依赖项视图 (Dependency View):随时可以从“视图”(View)->“其他窗口”(Other Windows)菜单中访问。如图 6 所示,它包含一个树状视图,按库列表(Library List)顺序列出程序集依赖项。但是,只有在您打开项目绘制器(即使之后关闭了它)或从项目绘制器对象上下文菜单检查了依赖项后,它才会被填充。请注意,依赖项检查器将其结果缓存到一个名为 `object_dependency.xml` 的文件中,位于 `.out` 文件夹中。如果文件不存在且您开始构建,则构建过程会自动调用“检查依赖项”(Check Dependencies)。
实现它需要什么
配置程序集不是一个自动过程。您需要确定程序集间的依赖关系,确定是否存在任何循环依赖,重新组织您的代码对象存储方案以消除它们,并确定全局类型(global types)的存储位置。
在分配 PBL 到程序集之前需要了解的内容
- 您的应用程序代码。您需要了解代码在哪里,什么依赖于什么,什么继承自什么,什么引用了什么。您可能需要将代码对象移到新的或不同的 PBL 中,并且必须明智地进行。如果您不确定依赖关系,请不要担心。有一个工具可以帮助您发现它们。但是,您提前了解得越多,配置过程就会越快。
- 您的应用程序使用的全局数据。当然,五个全局系统服务类型 - transaction (SQLCA)、dynamicstagingarea (SQLSA)、dynamicdescriptionarea (SQLDA)、message 和 error - 都涉及其中。您是否更改了任何全局服务的类型为自定义类型?如果是,该类型位于何处?您的 PBL 中是否定义了其他专门的应用程序全局类型?
- 哪些代码对象最有可能被修改?为了最大限度地缩短维护期间的增量构建时间,您需要将正在修改的代码对象隔离到单独的程序集中。
那些该死的依赖项!
程序集之间的依赖关系由引用形成。继承关系(`w_sheet FROM w_anc`)和变量类型声明(`w_anc Lw_anc`)都会产生依赖关系。您必须安排您的代码对象以避免程序集之间的循环依赖。表 1 解释了哪些引用是合法的,哪些是非法的。
那些全局变量怎么样?
如果您选择从全局变量创建一个程序集,它将位于程序集树的末尾。这使得每个程序集和 EXE 程序集都可以依赖于它。但是,全局程序集与其依赖的程序集之间不能存在循环依赖,也就是说,如果一个全局类型继承自另一个程序集中定义的某个内容,那么该全局类型就不能存在于自己的 DLL 中。为了解决这个问题,请指定将全局变量放入它们所依赖的程序集中。例如,`pfc` 中的 `n_tr` 是一个依赖于 `pfc_n_tr` 的全局类型。将全局变量放入与 PFC 相同的 PBL 中(有关完整的 PFC 解决方案,请参见下文)。
如果您不选择从全局变量创建程序集,那么每个程序集将拥有自己的全局变量集,也就是说,每个程序集都需要自己的数据库连接和其他全局资源。这肯定不是预期的结果。
资源怎么样?
应用程序资源分为两类:内部资源和外部资源。DataWindow 对象(和 Query 对象)被称为内部资源。图像(和其他项)的构建操作在其属性表中设置为“嵌入式资源”(embedded resource)(请参见图 7)的,被称为外部资源。
在项目绘制器(Project Painter)的程序集选项卡(如图 8 所示)中勾选“为可执行文件创建仅资源程序集”(Create resource-only assembly for executable)复选框,会将所有存储在计划进入 EXE 的 PBL 中的内部资源(即 DataWindow 对象)构建到一个单独的 `.resource` DLL 中。在 12.0 版本中,当时唯一的构建选项是单个 EXE,此选项是一个重要的节省时间和资源的功能。取消勾选此复选框将导致内部资源包含在 EXE 中。
外部资源的处理方式不同。`BuildActionAction=EmbeddedResource` 的项会被构建到每个输出程序集中,包括 EXE,但不会构建到仅资源程序集中。没有可用的映射工具可以将特定项定向到单个特定程序集。
图 9 显示了一个小型应用程序,当启用和禁用仅资源程序集时,该应用程序包含一个 DWO 和几个嵌入式资源图像的大小差异。
理解依赖项显示
依赖项显示窗口(如图 10 所示)包含一个帮助键,解释如何阅读依赖项图。
图 11 展示了完整的 IDE 及其视图,解释了其中的含义。
`WIP_RAD.exe` 程序集依赖于 `Base.dll` 程序集。`Base.dll` 程序集包含 `Base` PBL。`Base` PBL 包含 `w_window`,该窗口被应用程序对象 `WIP_RAD` 的 `open` 事件所引用。
`Base.DLL` 依赖于 `globals.DLL`。`Globals.DLL` 包含 `WIP_RAD` PBL(是的,它存在于两个地方,全局部分在一个程序集中,代码部分在另一个程序集中)。`n_customnonvisual` 的构造函数事件中的代码行引用了 `globals.dll` 中的对象。在这种情况下,它们是对 `SQLCA` 属性的赋值语句。