PAYATHON 2026

多团队如何协作开发 Microsoft Dynamics CRM 解决方案并实现持续发布?

技术老齐

对于 CRM 2016,建议将 CRM 自定义项和 C# 代码一起纳入 TFS。通过 Solution 导出、SolutionPackager 解包、代码评审和自动构建,生成统一的发布制品,再依次部署到集成、测试、验收和生产环境。

两个团队可以继续使用独立的开发环境,但要在日常开发中完成配置合并,再由集成环境验证合并后的版本。自动化能减少手工操作,跨团队功能合并后的集成测试仍然需要保留。

这套方案适用于 Microsoft Dynamics CRM 2016。SDK、工具和认证方式应与实际 CRM 版本及部署方式匹配,不能直接照搬新版 Dataverse 或 Power Platform 的流水线配置。

为什么只管理 C# 源代码不够

CRM 应用的行为同时取决于程序集和平台中的自定义项,包括:

  • 实体、字段、关系、表单和视图。
  • Web Resources、业务规则和工作流。
  • 插件程序集、插件步骤及其注册设置。
  • 安全角色等可包含在 Solution 中的组件。

如果 TFS 只保存 C#,就无法从某次提交重建完整应用,也难以判断某次发布包含了哪些配置变化。

还需要区分两类容易混淆的“配置”:字段定义属于元数据,可以通过 Solution 迁移;自定义配置实体中的记录属于数据,通常需要另外迁移。连接地址、凭据等环境设置也要单独处理。

先约定两个团队的修改边界

可以按业务能力划分 Solution,例如:

Solution内容维护方式
Company.Core共享实体、公共字段、公共组件两个团队共同评审
Company.Sales销售相关功能团队 A 负责
Company.Service服务相关功能团队 B 负责

对于规模较小、组件高度重叠的应用,也可以共同维护一个 Solution。具体如何划分,应根据依赖关系和发布节奏决定,不必与团队数量一一对应。

两个团队需要约定:

  1. 固定 Publisher、命名前缀和组件命名规则。共享组件应从共同基线导入,避免在两个环境中分别创建同一业务字段。
  2. 明确共享组件的负责人。需要同时修改同一表单或工作流时,先协调修改范围。
  3. 控制 Solution 内容。按目标版本支持的组件粒度选择所需内容,避免带入整套无关自定义。
  4. 定期同步已经集成的共享组件,避免两个开发环境长期偏离。

同一组件即使放进两个 Solution,也不会形成两个互相隔离的版本。Solution 之间还可能存在依赖,需要处理组件冲突,并安排好导入顺序。

将 CRM 自定义项纳入 TFS

开发环境通常使用 Unmanaged Solution,便于修改和导出。每次提交配置变更时,按以下步骤操作:

  1. 在开发环境完成修改,并发布需要导出的自定义项。
  2. 导出对应的 Unmanaged Solution。
  3. 使用与 CRM 2016 匹配的 SolutionPackager 解包。
  4. 将解包文件与相关 C#、JavaScript 变更一起提交到 TFS。
  5. 在代码评审中检查组件范围、依赖变化和配置差异。

仓库可以采用这样的结构:

src/
  Plugins/
  WorkflowActivities/
  WebResources/
crm/
  Core/
  Sales/
  Service/
deployment/
  scripts/
  configuration-data/
tests/

版本库中应保存解包后的文件,方便查看差异和评审。发布用的 Solution ZIP 则作为构建制品保存。

PowerShell 调用示例如下,工具路径需要按构建代理上的实际安装位置调整:

$solutionPackager = "C:\Tools\CrmSdk\SolutionPackager.exe"

& $solutionPackager `
    /action:Extract `
    /zipfile:".\exports\CompanySales_unmanaged.zip" `
    /folder:".\crm\Sales" `
    /packagetype:Unmanaged

if ($LASTEXITCODE -ne 0) {
    throw "Solution extraction failed."
}

构建时,从已提交的目录打包:

& $solutionPackager `
    /action:Pack `
    /zipfile:".\artifacts\CompanySales_unmanaged.zip" `
    /folder:".\crm\Sales" `
    /packagetype:Unmanaged

if ($LASTEXITCODE -ne 0) {
    throw "Solution packaging failed."
}

SolutionPackager 负责拆包和打包,不会自动解决 CRM 组件的语义冲突。两个分支修改同一表单时,即使 XML 能完成文本合并,也需要导入 CRM,验证布局和行为。

从统一源码生成发布包

建议增加一个可重置的构建组织,专门组装发布包;长期运行的集成测试环境则负责测试。

一次构建可以按以下顺序执行:

  1. 从 TFS 获取指定提交。
  2. 编译插件和自定义工作流程序集,运行相关单元测试。
  3. 从仓库打包 Unmanaged Solution。
  4. 将构建组织恢复到规定基线,安装必要依赖。
  5. 按依赖顺序导入 Unmanaged Solution。
  6. 使用本次编译结果更新插件程序集,并核对插件类型、步骤和 Image。
  7. 发布自定义项,设置发布版本,导出 Managed Solution。
  8. 保存 Solution ZIP、数据迁移文件、部署脚本和版本清单。

第 6 步需要确认新编译的 DLL 已经包含在 Solution 中。如果仓库中的解包内容仍携带旧程序集,打包结果也可能是旧代码。可以通过 SolutionPackager 的映射机制替换文件,也可以在构建组织中通过 SDK 更新程序集后再导出。选定一种方式后,应固定到构建流程中。

如果插件程序集存储在 CRM 数据库中,且已包含在 Solution 中,就可以随 Solution 发布。外部网站、Windows 服务等配套程序仍需要独立的部署任务,但应与 CRM 包归入同一次发布。

构建组织需要定期恢复基线。持续叠加 Unmanaged 导入不会自动清除所有历史遗留组件,残留的依赖还可能让一个不完整的包通过构建。

在 TFS 中建立逐级发布流程

TFS Build 或 Release 的具体配置取决于现有 TFS 版本。可以先用脚本封装 SDK 和打包工具,再由流水线调用,不必预设系统支持 YAML。

推荐流程如下:

TFS 提交
  → 编译、单元测试、生成 Solution
  → 导入构建组织、更新程序集、导出 Managed Solution
  → 集成环境:部署并执行跨团队测试
  → 测试环境:执行回归测试
  → 验收环境:业务验收
  → 生产环境:批准后部署

集成、测试、验收和生产环境通常使用 Managed Solution,并限制直接修改,以便更早验证实际的安装和更新行为。

发布制品通过验证后,后续环境应使用同一份制品。如果在部署验收或生产之前重新从团队开发环境导出,部署内容可能已经发生变化。

每个部署阶段至少需要:

  • 检查目标 CRM 版本、前置 Solution 和部署权限。
  • 按依赖顺序导入 Solution,保存导入日志。
  • 应用该环境的数据和设置,按部署计划激活工作流或相关组件。
  • 执行冒烟测试,并将结果关联到发布版本。

生产发布可以保留人工批准。持续发布要求过程可重复、可追踪,每次提交是否进入生产仍可由发布审批决定。

用 SDK 自动导入 Solution

CRM 2016 可以通过 Organization Service 调用 ImportSolutionRequest。导入脚本的核心操作如下:

using System;
using System.IO;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;

public static class SolutionDeployment
{
    public static Guid Import(
        IOrganizationService service,
        string solutionPath)
    {
        if (service == null)
            throw new ArgumentNullException(nameof(service));

        var importJobId = Guid.NewGuid();

        var request = new ImportSolutionRequest
        {
            CustomizationFile = File.ReadAllBytes(solutionPath),
            ImportJobId = importJobId,
            OverwriteUnmanagedCustomizations = false,
            PublishWorkflows = false
        };

        service.Execute(request);
        return importJobId;
    }
}

示例假定调用方已经建立连接,并引用了匹配版本的 CRM SDK。部署程序还需要补齐认证、超时、异常处理和流水线退出码。

PublishWorkflows = false 表示这一步不激活工作流,后续应按发布计划处理。即使设置了 OverwriteUnmanagedCustomizations = false,目标环境中已有的未托管自定义仍可能影响最终行为,因此还需要管理目标环境中的直接修改。

部署程序应保存 importJobId,读取对应 importjob 的日志,并保留诊断信息。若采用异步导入,必须等待任务完成后再判断部署是否成功。

构建组织导出托管包时,可以使用 ExportSolutionRequest,设置 Solution 的唯一名称和 Managed = true,并保存响应中的 ExportSolutionFile。导出前,应确认本次编译的程序集已经更新,自定义项也已发布。

单独管理配置数据和环境差异

Solution 无法替代所有数据迁移。对于自定义配置实体、参考数据等,可以评估 CRM 2016 对应 SDK 中的 Configuration Migration Tool,或编写专用迁移程序。

迁移前需要明确:

  • 用什么标识识别已有记录,避免重复创建。
  • 记录之间有何依赖,按什么顺序导入。
  • 再次执行时如何更新已有数据。
  • 哪些值允许覆盖,哪些值应由目标环境保留。

环境地址、账号和凭据应由部署配置或受控的机密存储提供。插件步骤的 Secure Configuration 等内容也需要检查,确认是否要单独部署,不能默认所有设置都会随 Solution 导出。

生产凭据不得写入源码或 Solution ZIP。服务地址等环境设置应单独管理,避免为此重新构建整套业务包。

删除、回退和测试要提前设计

普通 Solution 更新后,目标环境不一定与源码目录完全一致。从源 Solution 中移除字段或其他组件,再次导入时未必会删除目标组件。CRM 2016 不同更新级别支持的修补和升级流程需要分别确认,删除场景必须先在验证环境演练。

回退时,重新导入旧 ZIP 也未必能恢复原状。字段删除、数据转换和依赖变化都可能影响恢复;卸载 Managed Solution 还可能删除它引入的组件及相关数据。涉及这些变更时,应准备数据备份、恢复步骤或向前修复方案。

测试需要覆盖从规定基线安装完整解决方案,以及从当前生产版本升级到候选版本这两种情况。升级测试才能发现已有数据、历史配置和组件依赖带来的问题。

两个团队可以先确定共享组件边界,将 Unmanaged Solution 解包后提交到 TFS,再自动生成并导入可追踪的发布包。之后逐步加入配置数据迁移、升级验证和生产批准。每次发布都应能追溯到具体提交、配置内容和发布包,方便出现问题时排查。

备注:内容仅供参考。