维护正常运行的模型驱动应用窗体 ALM
本文介绍有关如何实施和实践正常应用程序生命周期管理 (ALM) 以自定义模型驱动应用解决方案中的窗体的各种方案。
以下章节介绍窗格合并的工作原理和如何维护自定义项。 后续每个章节中详细介绍了基本部署方案和有关为模型驱动应用窗体维护成功 ALM 的建议。 每个方案都包括可以执行的步骤,用于帮助您在更新解决方案或模型驱动应用时实施正确的 ALM 进程。
请执行以下步骤为此方案实施正常的窗体 ALM。
- 在开发环境中创建一个名称为 FormA 的新窗体,然后对该窗体执行自定义。
- 在开发环境中创建一个新解决方案(名称为 Solution A,这将是一个非托管解决方案),然后添加您的新窗体。 将该解决方案作为托管解决方案导出。 此步骤导出窗体的完整 FormXml。
- 在测试环境中,导入步骤 2 中的托管解决方案,从而在测试环境中创建 FormA。 在下面的关系图中,在测试环境中创建了 FormA,并且窗体的 UI 显示了 Solution A 向该窗体添加的 Field1 和 Field2。
- 当您使用新的开发(源)环境进一步自定义您在步骤 1 中创建的窗体时,导入在步骤 2 中创建的托管解决方案 A,确保您使用的开发实例的 FormA 处于托管状态。 如下面的关系图所示,将托管的 Solution A 导入了开发环境中,并且自定义了窗体以创建活动自定义项。 然后,可以将 FormA 添加到一个新的非托管解决方案(关系图中为 Solution B),并作为托管解决方案从开发环境中导出。 此步骤导出窗体的差异 (diff) FormXml。
- 在测试环境中,导入步骤 4 中的托管解决方案 (Solution B)。 如下面的关系图所示,Solution B 正在将一个新的 Field3 添加到 FormA,并且正在删除 Field2(这是 Solution A 添加的)。合并后,窗体在测试环境中的 UI 现在在窗体中显示 Field3 和 Field1,而不是显示 Field2。
如下面的关系图所示,从其中的基础解决方案 (Solution A) 处于非托管状态的开发环境创建多个托管解决方案不是正常的 ALM 实践。 这是因为,再为非托管窗体创建一个非托管解决方案 (Solution B) 时,FormXml 将作为完整 FormXml 导出,而不是作为差异 FormXml 导出,如上面的有效方案中所示。 因此,删除列这样的更改将不会生效。
请执行以下步骤为此方案实施正常的窗体 ALM。
在开发环境中创建一个名称为 FormA 的新窗体,然后对该窗体执行自定义。
创建一个解决方案(即下面的关系图中的 Solution A,这将是一个非托管解决方案),然后添加您的新窗体。 将该解决方案作为托管解决方案导出。 此步骤导出窗体的完整 FormXml。
在测试环境中,导入步骤 2 中的托管解决方案,从而在测试环境中创建该窗体。 在下面的关系图中,在测试环境中创建了 FormA,并且窗体的 UI 显示了 Solution A 向该窗体添加的 Field1 和 Field2。
使用修补程序进一步自定义步骤 1 中创建的窗体时,请使用其中的 Solution A 处于非托管状态的同一个环境,并为该解决方案创建一个修补程序,再自定义该窗体。 接下来,将该修补程序作为托管解决方案导出。 此步骤导出窗体的完整 formXml。
在测试环境中,导入步骤 4 中的托管修补程序解决方案。 如下面的关系图所示,Solution A 修补程序向 FormA 添加一个新的 Field3,并删除 Field2(这是 Solution A 添加的)。
备注
包含完整 formXml的补丁始终与从中创建补丁的基础层进行比较,并忽略基础补丁和当前补丁之间的任何中间补丁。 因此,会删除 Field2,因为它存在于基础层 Solution A 中,并且会检测到该删除操作。 另一方面,Field3 会由此补丁解决方案添加,并且无法被后续补丁删除。 因此,通过补丁解决方案添加的字段本质上是相加。
使用升级进一步自定义步骤 1 中创建的窗体时,请使用其中的 Solution A 处于非托管状态的同一个环境,并克隆 Solution A 以创建升级解决方案和自定义该窗体。 然后,将 Solution A 升级作为托管解决方案导出。 此步骤导出窗体的完整 FormXml。
在测试环境中,导入步骤 6 中的托管 Solution A 升级。 如下面的关系图所示,Solution A 升级正在将一个新的 Field4 添加到 FormA,并且正在删除 Field2(这是 Solution A 添加的)。从导入合并后,窗体在测试环境中的 UI 现在在窗体中显示 Field1、Field3 和 Field4,而不是将删除 Field2。
请执行以下步骤为此方案实施正常的窗体 ALM。
- 在开发环境中编辑一个现有托管窗体(本示例中名称为 FormB),然后对该窗体执行自定义。 请注意,Solution A 是已经在开发环境中为窗体安装的托管解决方案。
- 创建一个新解决方案(即下面的关系图中的 Solution B,这是一个非托管解决方案),然后添加 FormB。 将该解决方案作为托管解决方案导出。 此步骤导出窗体的差异 (diff) FormXml。
- 在测试环境中,导入步骤 2 中的托管解决方案,从而为窗体再创建一个解决方案层。 在下面的关系图中,FormB 在测试环境中获取从 Solution A 和 Solution B 合并的更改,并且窗体的 UI 在窗体中显示 Field1 和 Field3,而不显示 Field2(已被 Solution B 删除)。
- 在使用新托管解决方案进一步自定义在步骤 1 中自定义的窗体时,请确保使用 FormB 为托管状态的新开发环境。 如下面的关系图所示,将把解决方案 A 和解决方案 B 托管解决方案导入新开发环境中。 FormB 是自定义的,创建活动的自定义项,然后可以将其添加到新解决方案(图中的解决方案 C )并导出为托管解决方案。
- 在测试环境中,导入步骤 4 中的托管 Solution C。 如下面的关系图所示,Solution C 正在将一个新的 Field4 添加到 FormB,并且正在删除 Field3(这是 Solution B 添加的)。窗体在测试环境中的 UI 现在在窗体中显示 Field1 和 Field4,而不是显示 Field2 和 Field3。
如下面的关系图所示,从其中包含您为同一个窗体创建的另一个托管解决方案的开发环境创建多个托管解决方案不是正常的 ALM 实践。 请注意,Solution B 处于非托管状态。 再为 FormB 创建一个非托管解决方案 (Solution C) 时,将把 FormXml 作为差异 FormXml 导出,如上面方案中的步骤 4 所示。 但是,FormB 中也包含来自 Solution B 的更改,您的新更改将覆盖这些更改。
例如,如下面的关系图所示,将把 Field3 添加到 Solution B 中的 FormB。但是现在当您在此环境中创建一个新的 Solution C(此时 Solution B处于非托管状态)并删除 Field3 时,也将在开发环境中删除 Field3。 导出解决方案时,不会在 diff FormXml 中跟踪 Field3 ,因为添加和删除此列的更改是在同一活动图层中进行的。 这意味着,在测试环境中导入托管 Solution C 时,窗体仍将呈现 Field3,因为差异 FormXml 始终不会将其记录为已删除(就像其在上面的正常窗体 ALM 方案中步骤 5 内删除)。 如果以这种方式执行窗体自定义,则开发环境会与测试环境不一致。
请执行以下步骤为此方案实施正常的窗体 ALM。
在开发环境中自定义一个现有托管窗体(本示例中名称为 FormB),然后对该窗体执行自定义。 请注意,Solution A 是已经在开发环境中为窗体安装的托管解决方案。
创建一个解决方案(Solution B,这是一个非托管解决方案),然后添加 FormB。 将该解决方案作为托管解决方案导出。 此步骤导出窗体的差异 FormXml。
在测试环境中,导入步骤 2 中的托管 Solution B,从而为窗体再创建一个解决方案层。 在下面的关系图中,FormB 将获取来自测试环境中的 Solution A 和 Solution B 的更改。 此外,FormB 的 UI 在窗体中显示 Field1 和 Field3,而不显示 Field2(已被 Solution B 删除)。
在使用修补程序解决方案进一步自定义已在步骤 1 中自定义的窗体时,可以使用步骤 1 中使用的同一个开发环境,其中的 Solution B 为非托管状态。 如下面的关系图所示,Solution A 处于托管状态,而 Solution B 则处于非托管状态。 将进一步自定义窗体,而您将为 Solution B 创建一个修补程序,并将您的窗体添加到此解决方案,以及将其作为托管修补程序解决方案导出。 此步骤导出一个差异 FormXml。
在测试环境中,导入步骤 4 中的托管修补程序 Solution B。 如下面的关系图所示,Solution B 修补程序向 FormB 添加一个新的 Field4,并删除 Field3(这是 Solution B 添加的)。
备注
修补程序可累加,并且不能从窗体删除组件,如列。 因此,将不会从窗体删除 Field3。 窗体在测试环境中的 UI 现在在窗体中显示 Field1、Field3 和 Field4,但不显示 Field2。
使用升级进一步自定义步骤 1 中创建的窗体时,请使用其中的 Solution B 处于非托管状态的同一个环境,并克隆 Solution B 以创建升级解决方案和自定义 FormB。 将该升级作为托管解决方案导出。 此步骤导出窗体的差异 FormXml。
在测试环境中,导入步骤 6 中的托管 Solution B 升级解决方案。 如下面的关系图所示,Solution B 升级正在将一个新的 Field5 添加到 FormB,并且正在删除 Field3(这是 Solution B 添加的)。窗体在测试环境中的 UI 现在在窗体中显示 Field1、Field4 和 Field5,但是已删除 Field2 和 Field3。
请执行以下步骤为此方案实施正常的窗体 ALM。
- 在 Development Environment 1 中,创建一个新的 FormA,并对该窗体执行自定义。
- 创建一个解决方案(即下面的关系图中的 Solution A,这将是一个非托管解决方案),然后添加您的新窗体。 将该解决方案作为非托管解决方案导出。 此步骤导出窗体的完整 FormXml。
- 在 Development Environment 2 中,导入步骤 2 中的非托管解决方案,从而在 Development Environment 2 中创建该窗体。 在下面的关系图中,创建了 FormA,并且窗体的 UI 显示了 Solution A 向该窗体添加的 Field1 和 Field2。
- 在 Development Environment 2 中进一步自定义窗体以在环境中创建有效自定义项,如添加一个名称为 Field3 的新列。 FormA 现在显示 Field1、 Field2 和 Field3。
- 在 Development Environment 1 中,也通过添加 Field4 进一步自定义窗体。 Development Environment 1 中的窗体的 UI 现在显示 Field1、Field2 和 Field4。
- 导出带有步骤 5 中进行的更改的非托管 Solution A。 此步骤导出窗体的完整 FormXml。
- 在 Development Environment 2 中,导入步骤 6 中的非托管 Solution A 升级。 由于要导入的解决方案中包含 FormA 的完整 FormXml,因此其将覆盖在 Development Environment 1 中进行的有效自定义。 因此,窗体现在仅显示 Field1、Field2 和 Field4,不显示 Field3(这是在 Development Environment 1 中进行的额外有效自定义)。 具有窗体的完整 FormXml 的任何非托管解决方案都会发生此行为。
请执行以下步骤为此方案实施正常的窗体 ALM。
- 在 Development Environment 1 中,自定义一个现有窗体(此示例中名称为 FormB)。 然后对窗体执行自定义。
- 创建一个解决方案(即下面的关系图中的 Solution B,这将是一个非托管解决方案),然后添加 FormB。 将该解决方案作为非托管解决方案导出。 此步骤导出窗体的差异 FormXml。
- 在 Development Environment 2 中,导入步骤 2 中的托管解决方案,从而为窗体再创建一个解决方案层。 合并窗体后,FormB UI 显示 Field1、Field2 和 Field3。
- 在 Development Environment 2 中进一步自定义窗体以在环境中创建有效自定义项,如添加一个名称为 Field4 的新列。 FormB 现在显示 Field1、 Field2、 Field3 和 Field4。
- 在 Development Environment 1 中,通过添加一个名称为 Field5 的新列进一步自定义窗体。 Development Environment 1 中的窗体的 UI 现在显示 Field3 和 Field5。
- 导出带有步骤 5 中进行的更改的非托管 Solution B。 此步骤导出窗体的差异 FormXml。
- 在 Development Environment 2 中,导入步骤 6 中的非托管 Solution B 升级。 由于要导入的解决方案中包含 FormB 的差异 FormXml,因此其将与在 Development Environment 1 中进行的有效自定义合并。 因此,窗体现在显示 Field1、Field2、Field3、Field4 和 Field5。 具有窗体的差异 FormXml 的任何非托管解决方案都会发生此行为。
- 如果即使您要导入具有非托管解决方案的差异 FormXml 也不需要步骤 7 中的窗体合并,并且希望可以覆盖在 Development Environment 2 中进行的有效自定义,请删除 FormB 的活动层。 详细信息:删除非托管层。
- 导出带有步骤 5 中进行的更改的非托管 Solution B。 此步骤导出窗体的差异 FormXml。
- 在 Development Environment 2 中,导入步骤 9 中的非托管 Solution B 升级。 由于在 Development Environment 2 中没有窗体的有效层(参见步骤 8),所以将导入来自非托管 Solution B 的所有更改,即使您将导入 FormB 的差异 FormXml 也不例外。 因此,窗体现在仅显示 Field1、Field2、Field3 和 Field5。 具有窗体的差异 FormXml 的任何非托管解决方案都会发生此行为。 这是与跨多个开发环境维护现有窗体的非托管解决方案和自定义项方案中的步骤 7 相同的结果。
导出的每个解决方案包中都包含一个 customizations.xml 文件。 只要解决方案中包含窗体,customizations.xml 文件的 FormXml 部分内都会有相关窗体定义。 FormXml 可以是完整或差异 (diff)。
为非托管状态的窗体导出解决方案时获取的 FormXml 称为完整 FormXml。 完整意味着其中包含整个窗体定义。 新建并导出窗体时,窗体将始终为完整 FormXml,因为您正在从中进行导出的环境中的窗体处于非托管状态,并且也处于创建状态。 如果从从这同一个环境导出更多解决方案,这些解决方案中也将包含完整 FormXml。 由于 solutionaction
属性指示差异 FormXml,所以您导出的解决方案中的 customization.xml 文件内的完整 FormXml 将不包含任何 solutionaction
属性。
为托管状态的窗体导出解决方案时获取的 FormXml 称为差异或 diff FormXml。 差异意味着 FormXml 中仅包含该环境中有效自定义项进行的更改,而不是包含整个窗体定义。 自定义现有托管窗体并导出时,该窗体始终为差异 FormXml,因为其中仅包含对其进行的有效更改。 导出的解决方案中 customization.xml 文件内的差异 FormXml 中将包含 solutionaction
属性,用于定义更改是什么,如已添加、已删除或已修改。
差异 FormXml 可以确保解决方案仅表达您的应用所需更改,并较少受到其他层的更改的影响。 差异 FormXml 还可以精简解决方案,帮助提高其导入速度。