deploy

部署 Apex

您无法在 Salesforce 生产组织中开发 Apex。您的开发工作是 在沙盒或 Developer Edition 组织中完成。您可以使用以下方法部署 Apex:

  • 更改集
  • Salesforce 扩展 针对 Visual Studio Code
  • 蚂蚁迁徙 工具
  • 肥皂 应用程序接口
  • 使用元数据 API 或工具 API 的第三方工具
  • 带有 Salesforce DX 插件的 VS Code

部署时编译

从 18 年夏季开始,每个组织的 Apex 代码现在都会自动重新编译 完成元数据部署、包安装或包升级。部署时编译 为生产组织自动启用,以确保用户不会 部署后立即体验性能下降,而您不能 禁用它。对于沙盒、开发人员、试用和临时组织,此功能是 默认情况下禁用,但您可以在“设置”中的“Apex 设置”下启用。

此功能会导致部署到组织调用 Apex 编译器并保存 生成的字节码作为部署的一部分。部署时间的增加最小 ,但 Apex 不需要在首次运行时重新编译。所以轻微的 增加部署时间可以防止首次运行时出现性能问题。考虑 在多个用户共享的沙盒或临时组织中启用此功能 功能测试或由持续集成流程使用。

有关使用元数据 API 设置此组织首选项的信息,请参阅《元数据 API 开发人员指南》中的 OrgPreferenceSettings。

  1. 使用更改集部署 Apex
  2. 使用适用于 Visual Studio Code 的 Salesforce 扩展部署 Apex
  3. 使用 Ant 迁移工具部署更改
  4. 使用 SOAP API 部署Apex
    默认情况下,这些 Salesforce 对象和 SOAP API 调用和标头可用于 Apex。有关所有其他 SOAP API 调用的信息,包括可用于扩展或实现任何现有 Apex IDE 的调用,请联系您的 Salesforce 代表。

使用更改集部署 Apex

适用于:Salesforce Classic
提供 EnterprisePerformanceUnlimited 和 Database.com Edition

您可以部署 Apex 连接组织之间的类和触发器,例如,从沙盒组织到 您的生产组织。您可以在 Salesforce 用户界面中创建出站更改集 并添加要上传并部署到目标的 Apex 组件 组织。要了解有关更改集的更多信息,请参阅 Salesforce Online 中的“更改集” 帮助。

使用适用于 Visual Studio Code 的 Salesforce 扩展进行部署 顶点

适用于 Visual Studio Code 的 Salesforce 扩展包括用于在 轻量级、可扩展的 VS Code 编辑器中的 Salesforce 平台。这些工具提供 用于与开发组织(临时组织、沙盒和 DE 组织)、Apex、Aura 合作的功能 组件和 Visualforce。

注意

如果部署到生产组织:

  • 单元测试必须至少覆盖 75% 的 Apex 代码,并且所有这些测试都必须 成功完成。
  • 每个触发器都必须具有一定的测试覆盖率。
  • 所有类和触发器都必须成功编译。

请注意以下事项。

  • 将 Apex 部署到生产组织时,每个单元测试 默认情况下,将执行 Organization 命名空间。
  • 调用不计入 Apex 代码的一部分 覆盖。System.debug
  • 测试方法和测试类不计为 Apex 代码的一部分 覆盖。
  • 虽然只有 75% 的 Apex 代码必须被测试覆盖,但不要专注于 所覆盖代码的百分比。相反,请确保每次使用 涵盖您的申请案例,包括正面和负面案例, 以及批量和单条记录。这种方法可确保 75% 或更多 的代码包含在单元测试中。

有关如何使用 Visual Studio Code 部署到 Salesforce 组织的详细信息,请参阅开发模型。

使用 Ant 迁移工具部署更改

除了适用于 Visual Studio Code 的 Salesforce 扩展之外,您还可以使用脚本来执行以下操作 部署Apex。

下载 Ant 迁移工具以执行基于文件的元数据更改部署,以及 Apex 类从 Developer Edition 或沙盒组织到使用 Apache 的 Ant 构建工具。

注意

Ant 迁移工具是 Salesforce 提供的免费资源 支持其用户和合作伙伴,但出于目的不被视为我们服务的一部分 Salesforce 主要服务协议。要使用 Ant 迁移工具,请执行以下操作:

  1. 访问 http://www.oracle.com/technetwork/java/javase/downloads/index.html 并安装 Java JDK。注意Ant Migration Tool 版本 51.0 及更高版本需要 Java 版本 11 或更高版本。如果使用 Ant 迁移工具版本 36.0 到 50.0,为了增强安全性,我们 建议使用 Java 7 或更高版本以及最新版本的 Ant 迁移工具(版本 36.0 或更高版本)。从版本 36.0 开始,Ant 迁移工具使用 TLS 1.2 来确保安全 当 Salesforce 检测到 Java 版本 7 (1.7) 时与 Salesforce 进行通信。工具 显式启用 Java 7 的 TLS 1.1 和 1.2。如果您使用的是 Java 8 (1.8)、TLS 1.2 被使用。对于 Java V6,使用 TLS 1.0,而 Salesforce的。或者,如果您使用的是 Java 7,而不是升级 Ant 迁移工具 对于版本 36.0 或更高版本,您可以将以下内容添加到环境变量中:ANT_OPTS-Dhttps.protocols=TLSv1.1,TLSv1.2此设置还会对本地的任何其他 Ant 工具强制实施 TLS 1.1 和 1.2 系统。
  2. 访问 http://ant.apache.org/ 并安装 Apache Ant 版本 1.6 或更高版本,在部署计算机上。
  3. 按照《Ant 安装指南》第 http://ant.apache.org/manual/install.html 页中指定的方式设置环境变量(如 、 和 )。ANT_HOMEJAVA_HOMEPATH
  4. 通过打开命令提示符来验证 JDK 和 Ant 是否已正确安装,然后 进入。您的输出 必须看起来像 这:ant –versionApache Ant version 1.7.0 compiled on December 13 2006
  5. 下载21年夏季蚂蚁迁徙的.zip文件 工具。下载链接没有 要求对 Salesforce 进行身份验证。如果您已登录 Salesforce,我们建议您 在访问浏览器中的链接之前注销。
  6. 将下载的文件解压缩到您选择的目录。Zip 文件包含以下内容:
    • 说明如何使用这些工具的自述文件 .html 文件
    • 包含 ant 任务的 Jar 文件:ant-salesforce.jar
    • 包含以下内容的示例文件夹:
      • 一个 codepkg\classes 文件夹,其中包含SampleDeployClass.clsSampleFailingTestClass.cls
      • 包含 SampleAccountTrigger.trigger 的 codepkg\triggers 文件夹
      • 包含自定义对象的 mypkg\objects 文件夹 在示例中使用
      • 一个 removecodepkg 文件夹,其中包含要删除的 XML 文件 来自您组织的示例
      • 您必须编辑的示例 build.properties 文件, 指定您的凭据,以便在 build.xml 中运行示例 Ant 任务
      • 一个示例 build.xml 文件,用于执行 和 API 调用deployretrieve
  7. Ant 迁移工具使用 ant-salesforce.jar 文件 分发.zip文件。如果您安装了该工具的早期版本,并且 将 ant-salesforce.jar 复制到 Ant lib 目录,删除之前的 jar 文件。lib 目录位于 Ant 的根文件夹中 安装。您无需将新的 jar 文件复制到 Ant lib 目录。
  8. 打开解压缩文件中的示例子目录。
  9. 编辑 build.properties 文件:
    1. 分别在 sf.user 和 sf.password 字段中输入您的 Salesforce 生产组织用户名和密码。注意
      • 您指定的用户名必须具有编辑 Apex 的权限。
      • 如果您从不受信任的网络使用 Ant 迁移工具,请附加 安全令牌添加到密码中。了解有关安全性的详细信息 令牌,请参阅“重置” 您的安全令牌“在 Salesforce 帮助中。
    2. 如果要部署到沙盒组织,请将 sf.serverurl 字段更改为 .https://test.salesforce.com
  10. 在示例目录中打开命令窗口。
  11. 进入。这将使用示例类和 Account 运行 API 调用 触发器随 Ant 迁移工具提供。ant deployCodedeploy调用 build.xml 文件中命名的 Ant 目标。ant deployCodedeploy<!-- Shows deploying code & running tests for package 'codepkg' --> <target name="deployCode"> <!-- Upload the contents of the "codepkg" package, running the tests for just 1 class --> <sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" deployroot="codepkg"> <runTest>SampleDeployClass</runTest> </sf:deploy> </target>有关详细信息,请参阅了解部署。
  12. 要删除在执行过程中添加的测试类和触发器,请在命令窗口中输入以下内容: 。ant deployCodeant undeployCodeant undeployCode调用 build.xml 文件中命名的 Ant 目标。undeployCode<target name="undeployCode"> <sf:deploy username="${sf.username}" password="${sf.password}" serverurl= "${sf.serverurl}" deployroot="removecodepkg"/> </target>

查看 Ant 迁移工具 有关 Ant 迁移工具的完整详细信息的指南。

  1. 了解部署
    Ant 迁移工具提供了该任务,该任务可以合并到您的部署脚本中。deploy
  2. 了解检索

了解部署

Ant 迁移工具提供了该任务,该任务可以合并到您的部署脚本中。

deploy

您可以修改 build.xml 示例以包含组织的 类和触发器。有关部署任务的属性的完整列表,请参见《Ant 迁移工具指南》。该任务的一些属性包括:deployusername用于登录 Salesforce 生产组织的用户名。password与登录 Salesforce 生产组织关联的密码。serverURL您正在登录的 Salesforce 服务器的 URL。我们建议您指定 贵组织的 My Domain 登录 URL,列在“My Domain 设置”页面上。如果你不这样做 指定一个值,默认值为 。login.salesforce.comdeployRoot包含 Apex 类和触发器的本地目录,以及任何其他 元数据。创建必要文件结构的最佳方法是 从您的组织或沙盒中检索它。有关详细信息,请参阅了解检索。

  • Apex 类文件必须位于名为 classes 的子目录中。您必须有两个 每个类的文件,命名如下:
    • classname.CLS公司
    • classname.cls-meta.xml
    例如,和 .-meta.xml 文件包含 API 版本和类的状态(活动/非活动)。MyClass.clsMyClass.cls-meta.xml
  • Apex 触发器文件必须位于名为 triggers 的子目录中。你必须有 每个触发器有两个文件,命名如下:
    • triggername.触发
    • triggername.触发器元.xml
    例如,和 .-meta.xml 文件 包含触发器的 API 版本和状态(活动/非活动)。MyTrigger.triggerMyTrigger.trigger-meta.xml
  • 根目录包含一个 XML 文件包 .xml,其中列出了 要部署的所有类、触发器和其他对象。
  • 根目录(可选)包含一个 XML 文件 destructiveChanges.xml,其中列出了所有类、触发器、 以及要从组织中删除的其他对象。

checkOnly指定是将类和触发器部署到目标环境,还是 不。此属性采用布尔值:if 否则,您不希望将类和触发器保存到组织中。如果未指定值,则 缺省值为 。truefalsefalserunTest可选的子元素。包含部署后运行的测试的 Apex 类的列表。 要使用此选项,请将 testLevel 设置为 。RunSpecifiedTeststestLevel自选。指定在部署过程中运行哪些测试。测试级别为 无论部署包中存在的组件类型如何,都强制执行。 有效值为:

  • NoTestRun– 不运行任何测试。此测试 级别仅适用于部署到开发环境,例如沙盒、 Developer Edition 或试用组织。此测试级别是 开发环境。
  • RunSpecifiedTests– 仅运行您在 runTests 选项中指定的测试。代码覆盖率要求不同于 使用此测试级别时的默认覆盖率要求。每个类和触发器 在部署包中,必须至少覆盖 75% 的已执行测试 代码覆盖率。此覆盖率是针对每个类计算的,并单独触发,并且 与总体覆盖率不同。
  • RunLocalTests组织中的所有测试都是 运行,但源自已安装、托管和解锁的那些除外 包。此测试级别是生产部署的默认级别,该级别符合以下条件 包括 Apex 类或触发器。
  • RunAllTestsInOrg运行所有测试。 这些测试包括组织中的所有测试,包括托管测试 包。

如果未指定测试级别,则使用默认测试执行行为。 请参阅元数据 API 开发人员的 指南。

此字段在 API 版本 34.0 中可用,并且 后。runAllTests(已弃用,仅在 API 版本 33.0 及更早版本中可用。此参数是 可选,缺省值为 。设置为在部署后运行所有 Apex 测试,包括测试 源自已安装的托管软件包。falsetrue

了解检索

使用目标检索类和 来自沙盒或生产组织的触发器。在正常的部署周期中,您将在 之前运行,以便为新类和 触发器。但是,对于此示例,首先用于 确保有要检索的内容。retrieveCoderetrieveCodedeploydeploy

若要从现有组织中检索类和触发器, 使用检索 Ant 任务,如示例构建目标所示:ant retrieveCode

<target name="retrieveCode">
   <!-- Retrieve the contents listed in the file codepkg/package.xml into the codepkg directory -->
   <sf:retrieve username="${sf.username}" password="${sf.password}" 
        serverurl="${sf.serverurl}" retrieveTarget="codepkg" unpackaged="codepkg/package.xml"/>
</target>

文件 codepkg/package.xml 列出了元数据 要检索的组件。在此示例中,它检索两个类 和一个触发器。检索到的文件被放入目录 codepkg 中,覆盖目录中已有的所有内容。

检索任务的属性如下:

描述
用户名如果 sessionId 不是,则为必填项 指定。用于登录的 Salesforce 用户名。与此关联的用户名 连接必须具有通过元数据 API 函数修改元数据 许可。
密码如果 sessionId 不是,则为必填项 指定。用于登录与此关联的组织的密码 项目。如果您使用的是安全令牌,请将 25 位令牌值粘贴到 密码末尾。
会话 ID如果未指定用户名和密码,则为必填项。活动 Salesforce 会话或 OAuth 的 ID 访问令牌。用户成功登录 Salesforce 后将创建会话 使用用户名和密码。使用会话 ID 登录到现有会话 而不是创建新会话。或者,将访问令牌用于 OAuth 认证。有关更多信息,请参阅使用 Salesforce 帮助中的 OAuth。
服务器网址自选。Salesforce 服务器 URL(如果为空, 默认值为 )。连接 更改为沙盒实例,请将此值更改为 。login.salesforce.comtest.salesforce.com
检索目标必填。目录结构的根目录 元数据文件被检索到其中。
包名称如果未指定 unpackaged,则为必填项。逗号分隔 要检索的包的名称列表。指定 packageNames 或 unpackaged,但不指定 双。
api版本自选。要用于 检索到的元数据文件。默认值为 59.0。
轮询WaitMillis自选。缺省值为 .数量 轮询检索结果时两次尝试之间等待的毫秒数 请求。客户端继续轮询服务器,直至达到 maxPoll 定义的限制。10000
最大投票自选。缺省值为 .轮询服务器以获取结果的次数 检索请求。200连续轮询尝试之间的等待时间由 pollWaitMillis 定义。
单包自选。缺省值为 .设置此参数 以检索多个包。 如果设置为 ,则检索到的 zip 文件包括 额外的顶级目录,包含每个包的子目录。truefalsefalse
跟踪自选。缺省值为 . 将 SOAP 请求和响应打印到控制台。此选项显示用户的 登录时以纯文本形式输入密码。false
未包装如果未指定 packageNames,则为必需。路径和名称 指定要检索的组件的文件清单。指定 unpackaged 或 packageNames,但不能同时指定两者。
解 压缩自选。缺省值为 .如果设置为 ,则检索到的 组件已解压缩。如果设置为 ,则 检索到的组件将作为 zip 文件保存在 retrieveTarget 目录中。truetruefalse

使用 SOAP API 部署Apex

这些 Salesforce 对象和 SOAP API 调用和标头是 默认情况下可用于 Apex。有关所有其他 SOAP API 调用的信息,包括那些 可用于扩展或实施任何现有的 Apex IDE,请联系您的 Salesforce 代表。

Apex 类方法可以公开为自定义 SOAP Web 服务调用。这允许外部 应用程序调用 Apex Web 服务以在 Salesforce 中执行操作。使用关键字来定义这些方法。为 有关详细信息,请参阅使用 webservice 关键字的注意事项。webservice使用 SOAP API 调用保存的任何 Apex 代码都使用与端点相同的 SOAP API 版本 的请求。例如,如果要使用 SOAP API 版本 59.0 ,请使用端点 59.0:

https://MyDomain.salesforce.com/services/Soap/s/59.0

这些 Salesforce 对象可用于 Apex。

  • ApexTestQueueItem
  • ApexTestResult(阿佩克斯测试结果)
  • ApexTestResultLimits
  • ApexTestRunResult

使用这些 SOAP API 调用来部署 Apex。

  • compileAndTest()
  • compileClasses()
  • compileTriggers()
  • executeanonymous()
  • runTests()

所有这些调用都采用包含类或触发器的 Apex 代码,以及 需要设置的任何字段的值。这些 SOAP 标头在 Apex 的 SOAP API 调用中可用。

  • DebuggingHeader
  • PackageVersionHeader

另请参阅元数据 API 开发人员指南,了解另外两个调用:

  • deploy()
  • retrieve()

使用托管软件包分发 Apex

作为 ISV 或 Salesforce 合作伙伴,您可以将 Apex 代码分发给客户 使用包的组织。在这里,我们将介绍包和包 版本控制。

重要

如果一个类有一个 依赖于 Chatter,代码可以编译并安装在没有 启用 Chatter。但是,如果未启用 Chatter,则代码会在运行时抛出错误 时间。请参阅打包 ConnectApi 类。ConnectApi

  1. 什么是套餐?
  2. 软件包版本
  3. 弃用 Apex
  4. 包版本中的行为

什么是套餐?

是一个容器,用于容纳小到单个组件或 大作为一组相关应用。创建包后,可以将其分发给其他 Salesforce 用户和组织,包括公司外部的用户和组织。组织可以 创建一个可由多个不同用户下载和安装的托管软件包 组织。托管包与非托管包的不同之处在于具有一些锁定的组件, 允许以后升级托管包。非托管包不包括锁定的包 组件,并且无法升级。

软件包版本

包版本是一个数字,用于标识包中上载的组件集。这 版本号的格式为 (对于 例如,2.1.3)。在每个主要数字中,主要数字和次要数字增加到选定的值 释放。仅针对补丁版本生成和更新。majorNumber.minorNumber.patchNumberpatchNumber

非托管包不可升级,因此每个包版本只是一组组件 用于分发。包版本对于托管包具有更重要的意义。包 对于不同的版本,可以表现出不同的行为。发布者可以使用包 版本,通过发布来优雅地发展其托管包中的组件 后续软件包版本,而不会破坏现有客户集成,使用 包。

当现有订阅者安装新的包版本时,仍然只有一个实例 包中的每个组件,但这些组件可以模拟旧版本。为 例如,订阅者可以使用包含 Apex 类的托管包。如果 发布者决定弃用 Apex 类中的方法并发布一个新包 版本,订阅者在安装后仍然只能看到 Apex 类的一个实例 新版本。但是,此 Apex 类仍然可以模拟任何 在旧版本中引用已弃用方法的代码。

在托管软件包中开发 Apex 时,请注意以下事项:

  • 包含在 Apex 类、触发器或 Visualforce 组件中的代码,该组件是 托管软件包被模糊处理,无法在安装组织中查看。唯一的例外是 声明为 global 的方法。您可以在安装组织中查看全局方法签名。在 此外,具有“查看和调试托管 Apex”权限的许可证管理组织用户可以查看 当通过订阅者登录订阅者组织时,他们的软件包会混淆 Apex 类 支持控制台。
  • 托管包接收唯一的命名空间。此命名空间在类名之前, 方法、变量等,这有助于防止安装程序组织中出现重复名称。
  • 在单个事务中,只能引用 10 个唯一命名空间。例如,假设 您有一个对象,该对象在更新对象时执行托管包中的类。 然后,该类更新第二个对象,该对象又在不同的 包。即使第一个包没有直接访问第二个包,访问 发生在同一事务中。因此,它包含在 单笔交易。
  • 包开发人员可以使用已弃用的注释来标识 无法再引用的方法、类、异常、枚举、接口和变量 在它们所在的托管包的后续版本中。这在以下情况下很有用 随着需求的发展重构托管包中的代码。
  • 可以编写测试方法,将包版本上下文更改为其他包 版本,使用系统方法。runAs
  • 在以下情况下,不能将方法添加到全局接口,也不能将抽象方法添加到全局类 接口或类已在托管 – 已发布包版本中上传。如果 Managed – Released 包中的类是虚拟的,您可以添加该方法 它也必须是虚拟的,并且必须有一个实现。如果 托管 – 发布包扩展了另一个类,现有类协定不能 被删除。
  • 非托管包中包含的 Apex 代码 显式引用命名空间无法上传。

弃用 Apex

包开发人员可以使用已弃用的注释来标识 无法再引用的方法、类、异常、枚举、接口和变量 在它们所在的托管包的后续版本中。这在以下情况下很有用 随着需求的发展重构托管包中的代码。 将另一个包版本上传为托管版本后 – 已发布,安装最新包版本的新订阅者看不到已弃用的 元素,而这些元素继续为现有订阅者和 API 运行 集成。弃用的项(如方法或类)仍可引用 由包开发人员在内部进行。

注意

您不能在 Apex 类中使用注释,也不能在非托管包中使用触发器。deprecated

包开发人员可以将托管 – Beta 包版本用于 对不同 Salesforce 组织中的一组试点用户进行评估和反馈。如果 开发人员弃用 Apex 标识符,然后将包的版本上传为托管 – Beta 版,安装包版本的订阅者仍会在其中看到已弃用的标识符 包版本。如果包开发人员随后上传托管 – 已发布的包版本, 订阅者在订阅后将不再在包版本中看到已弃用的标识符 安装它。

包版本中的行为

包组件可以在不同的 包版本。此行为版本控制允许您添加新组件 到您的包装中并优化您现有的组件,同时仍然确保 您的代码继续为现有订阅者无缝工作。 如果包开发人员将新组件添加到包中并上传 新的软件包版本,新组件可供订阅者使用 安装新的软件包版本。

  1. 对 Apex 代码行为进行版本控制
  2. 未进行版本控制的 Apex 代码项
  3. 在包版本中测试行为

对 Apex 代码行为进行版本控制

包开发人员可以在 Apex 类和触发器中使用条件逻辑 以显示不同版本的不同行为。条件逻辑允许包 开发人员支持以前包版本中的类和触发器中的现有行为 同时改进代码。

当订阅者安装包的多个版本并编写引用 Apex 的代码时 包中的类或触发器,它们必须选择 他们引用的版本。在 包中引用的 Apex 代码,您可以有条件地执行 基于调用 Apex 代码的版本设置的不同代码路径 进行引用。调用代码的包版本设置可以是 通过调用该方法在包代码中确定。通过这种方式,包开发人员可以 确定请求上下文并为不同版本指定不同的行为 的包。System.requestVersion

以下示例使用该方法并实例化该类,以在 Apex 触发器中为不同的包版本定义不同的行为。System.requestVersionSystem.Version

trigger oppValidation on Opportunity (before insert, before update) {

    for (Opportunity o : Trigger.new){
    
        // Add a new validation to the package
        // Applies to versions of the managed package greater than 1.0
        if (System.requestVersion().compareTo(new Version(1,0)) > 0) {
            if (o.Probability >= 50 && o.Description == null) {
                o.addError('All deals over 50% require a description');
            }
        }

        // Validation applies to all versions of the managed package.
        if (o.IsWon == true && o.LeadSource == null) {
            o.addError('A lead source must be provided for all Closed Won deals');
        }
    }
}

有关使用包版本的方法的完整列表,请参阅 Version 类和 System Class 中的方法。System.requestVersion

如果已安装包中的某个类调用另一个包中的方法,则会保留请求上下文 类。例如,订阅者安装了一个 GeoReports 包,该包 包含 CountryUtil 和 ContinentUtil Apex 类。订阅者创建一个新的 GeoReportsEx 类,并使用版本设置将其绑定到 2.3 版 GeoReports 包。如果 GeoReportsEx 在 ContinentUtil 中调用内部的方法 调用 CountryUtil 中的方法,请求上下文从 ContinentUtil 传播到 CountryUtil 和方法 在 CountryUtil 中返回 GeoReports 包的 2.3 版本。System.requestVersion

未进行版本控制的 Apex 代码项

您可以跨包版本更改某些 Apex 项目的行为。例如,您可以 弃用某个方法,以便新订阅者无法再在后续中引用该包 版本。

但是,无法对以下修饰符、关键字和批注列表进行版本控制。如果 包开发人员对以下修饰符、关键字或注释之一进行更改, 这些更改将反映在所有包版本中。

当这些项目出现以下情况时,您可以对其中某些项目进行的更改存在限制 在托管包的 Apex 代码中使用。

包开发人员可以添加或删除以下项:

  • @future
  • @isTest
  • with sharing
  • without sharing
  • transient

包开发人员可以对以下项进行有限的更改:

  • private—可以更改为global
  • public—可以更改为global
  • protected—可以更改为global
  • abstract– 可以更改为,但不能删除virtual
  • final– 可以删除,但不能删除 添加

程序包开发人员无法删除或更改以下项:

  • global
  • virtual

包开发人员可以添加关键字, 但是一旦添加,就无法删除。

webservice

注意

不能弃用托管方法或变量 软件包代码。webservice

在包版本中测试行为

当您更改不同包版本的 Apex 类或触发器中的行为时,这很重要 测试代码在不同包版本中是否按预期运行。您可以编写更改包的测试方法 使用系统将上下文版本更改为不同的包版本 方法。runAs您可以 仅在测试中使用 方法。runAs

以下示例显示了具有不同行为的触发器 不同的包版本。

trigger oppValidation on Opportunity (before insert, before update) {

    for (Opportunity o : Trigger.new){
    
        // Add a new validation to the package
        // Applies to versions of the managed package greater than 1.0
        if (System.requestVersion().compareTo(new Version(1,0)) > 0) {
            if (o.Probability >= 50 && o.Description == null) {
                o.addError('All deals over 50% require a description');
            }
        }

        // Validation applies to all versions of the managed package.
        if (o.IsWon == true && o.LeadSource == null) {
            o.addError('A lead source must be provided for all Closed Won deals');
        }
    }
}

以下测试类使用该方法验证触发器的行为(有和没有) 特定版本:runAs

@isTest
private class OppTriggerTests{

   static testMethod void testOppValidation(){
   
      // Set up 50% opportunity with no description
      Opportunity o = new Opportunity();
      o.Name = 'Test Job';
      o.Probability = 50;
      o.StageName = 'Prospect';
      o.CloseDate = System.today();
      
      // Test running as latest package version
      try{
          insert o;
      }
      catch(System.DMLException e){
          System.assert(
              e.getMessage().contains(
                'All deals over 50% require a description'),
                  e.getMessage());
      }
      
      // Run test as managed package version 1.0
      System.runAs(new Version(1,0)){
          try{
              insert o;
          }
          catch(System.DMLException e){
              System.assert(false, e.getMessage());
          }
      }

      // Set up a closed won opportunity with no lead source
      o = new Opportunity();
      o.Name = 'Test Job';
      o.Probability = 50;
      o.StageName = 'Prospect';
      o.CloseDate = System.today();
      o.StageName = 'Closed Won';
      
      // Test running as latest package version
      try{
          insert o;
      }
      catch(System.DMLException e){
          System.assert(
            e.getMessage().contains(
              'A lead source must be provided for all Closed Won deals'),
                e.getMessage());
      }

      // Run test as managed package version 1.0
      System.runAs(new Version(1,0)){
          try{
              insert o;
          }
          catch(System.DMLException e){
              System.assert(
                  e.getMessage().contains(
                    'A lead source must be provided for all Closed Won deals'),
                        e.getMessage());
          }
      }
   }
}