分类 Salesforce 下的文章

Managed Package有许多限制,比如无法在外部/本地代码中序列化受控包中的Apex Class

JSON Support Considerations

Only custom objects, which are sObject types, of managed packages can be serialized from code that is external to the managed package. Objects that are instances of Apex classes defined in the managed package can't be serialized.

假如有个包Px,命名空间Px。我们有个Apex Class需要调用里面的对象并JSON序列化。

直接JSON序列化是不行的

包中代码:

//Inside package Px
global class C {
  global String stringA;
  global Integer StringB;
}

本地代码调用,会失败:

@isTest
private class TestFromLocal {
    @isTest static void testSerialize() {
    try{ 
      JSON.serialize(new Px.C());
      System.assert(false,'This should fail');
    } catch (Exception ex) {
      System.assertEquals('Cannot serialize type from package: Px.C',ex.getMessage());
    }
  }
}

但是可以通过扩展Apex Class来序列化

我们可以利用扩展(extends)一个虚拟类(virtual class)来实现序列化!

1. 保证需要序列化的Class是virtual

包中代码:

//Inside package Px
global virtual class C {
  global String stringA;
  global Integer StringB;
}

2. extends it!

@isTest
private class TestFromLocal {

class Ext extends Px.C { }

@isTest static void testSerialize() {
  Ext virt = new Ext();
  virt.stringA = 'bla';
  virt.IntgerB = 1;
  System.assertEquals('{"stringA":"bla", "IntgerB": 1}',JSON.serialize(virt));
}

}

这样就可以正常调用了!

参考

1.Salesforce: Serializing Objects from a Managed Package

2GP Managed Package

Salesforce 第二代管控包(Second-Generation Managed Packages)是Salesforce最新推广的最佳实践。通过Salesforce CLI,我们可以很方便的制作和部署第二代管控包。本文只是简略的将核心步骤展示出来:

涉及的各种环境

整个第二代管控包需要用到4类org:

  1. Dev Hub Org:承载所有二代包。可以将各个命名空间的二代包链接到这个org。最好是PBO
  2. Namespace Org:一般是Developer org,在其中申请相应的命名空间,由于命名空间一旦申请便与该org永久绑定且不可修改,申请时请慎重。
  3. Scratch Orgs:在开发测试中使用。
  4. Production Orgs:按照和使用包的生产环境。

2GP Packages.drawio.svg
相关org关系示意图。

10步完成制作

  1. 创建一个 SFDX 项目

    sfdx force:project:create --outputdir expense-manager-workspace --projectname expenser-app
  2. 授权Dev Hub环境,该环境必须启用Dev Hub功能和未锁定包和第二代管控包功能

    sfdx auth:web:login -d -a devHub
  3. 创建一个草稿环境(scratch org)并在其中开发包

    sfdx force:org:create -f config/project-scratch-def.json -u scratchOrg1
  4. 保证所有要打包的组件都已经在当前的项目文件夹内
  5. sfdx-project.json文件中,使用命名空间属性指定命名空间。例如:“namespace”:“exp-mgr”
  6. 从 SFDX 项目文件夹,直接创建管控包:

    sfdx force:package:create --name "Expense Manager" --path force-app --packagetype Managed
  7. 检查项目文件夹中的sfdx-project.json文件,CLI 会自动更新项目文件,使其包含上一步创建的包的信息:

    {
    "packageDirectories": [
       {
          "path": "force-app",
          "default": true,
          "package": "Expense Manager",
          "versionName": "ver 0.1",
          "versionNumber": "0.1.0.NEXT"
       }
    ],
    "namespace": "exp-mgr",
    "sfdcLoginUrl": "https://login.salesforce.com",
    "sourceApiVersion": "51.0",
    "packageAliases": {
       "Expense Manager": "0Hoxxx"
    }
    }
  8. 创建一个包的版本,Salesforce CLI会自动处理包的版本号等

    sfdx force:package:version:create --package "Expense Manager" --installationkey test1234 --wait 10
  9. 在另一个创建好的草稿环境中,安装并测试这个版本的包:

    sfdx force:package:install --package "Expense [email protected]" --targetusername MyTestOrg1 
    --installationkey test1234 --wait 10 --publishwait 10
  10. 安装包后,打开scratch org查看包:

    sfdx force:org:open --targetusername MyTestOrg1
\frac{a}{b^2}

参考:

  1. Salesforce DX Developer Guide
  2. Link a Namespace to a Dev Hub Org
  3. Create and Register Your Namespace for Second-Generation Managed Packages
  4. Workflow for Second-Generation Managed Packages

Salesforce Advice

Reduce class level variables data storage

Don't use class level variables to store a large amounts of data.

User SOQL for Loops

Utilize SOQL For Loops to iterate and process data from large queries.

for(Conatct ctt : [Select Id,Name,Account.Name FROM Contact]) {
    // do your work
}

Shorten variables lifetime

Construct methods and loops that allow variables to go out of scope as soon as they are no longer needed.

Other Advice

Using Efficient Algorithms

Being the heart of any code, algorithms can make or break the running of your application. A good space-efficient algorithm can save a lot of your heap memory while simultaneously performing smoothly. Before developing any logic or writing any code snippet, sit, and think through the algorithms to use and choose the one that serves your purpose and makes use of the heap memory efficiently. Try not to split the parts of your algorithms into different functions; rather try to keep them in line when used only within that code.

Built-in Apex libraries

Try to leverage and make use of the inbuilt Apex libraries that Salesforce provides other than writing your custom logic for everything. For example, use the Math class for performing mathematical operations, use JSONGenerator class to create JSON structures, and many more. One line of method calling code is way better than rewriting the logic on your own. This not only saves you time but also the heap memory is managed efficiently.

Avoid using temporary variables

Creating temporary variables is a favorite practice of many developers. We unknowingly create such variables in our code without realizing that we can do without them. These variables take up unnecessary space and add up to the heap memory.

Shorten variable names & declarations

You can spare characters when naming temporary variables like loop counter variables as i instead of myCounter. You should keep meaningful names according to the business.
You can also combine variable declarations like:

Integer i = 0, j = 10;

Shorten Field API Name

We all love to give meaningful names to the custom fields in Salesforce objects. Little do we know that a long API name impacts the code heap size. For example, it is counted in SOQL query size, code that references the field, and each time it is accessed. Maximum length of SOQL statements is 100K.

Remove unnecessary debug statements

Since all the debug statements are counted against the code length, it is necessary to keep a cap on them in order to limit the consumed heap size of the Apex code. It is fine to use them while doing the apex development but should be removed or minimized in the final production version of the code.

Reference

有时候我们需要在Salesforce页面下载存放为Attachment的文件;或者我们需要把存成Attachment的图片显示在页面上。

内部站点

如果我们文件为Attachment,在Salesforce Internal (即使用Salesforce账户登录的,非Digital Experience, Site.com),则可以使用

 <a href="{!URLFOR($Action.Attachment.Download, attachmentId)}">File Name</a>

下载文件。

或者使用

<img src="{!URLFOR($Action.Attachment.Download, attachmentId)}" />

显示图片。

如果是File,则可以使用Url.getFileFieldURL(entityId, fieldName)

Site.com

Site.com上的则不能用上面的方法,使用的话会直接报错。这时可以通过Salesforce提供的文件下载api /servlet/servlet.FileDownload?file=xxxx~18,不过要注意,一定要使用Salesorce Internal Url,使用Site.com的相关url肯定也会报错。

  1. 首先在后台获取 Salesforce Instance Url 或 Org Domain Url。例如:
    String orgUrl = Url.getOrgDomainUrl().toExternalForm()
  2. 将其传到前台,拼接成下载地址:
    <a href="{!orgUrl + '/servlet/servlet.FileDownload?file=' + attachment.Id}">File Name</a>

参考文档

  1. Visualforce Developer Guide: $Action
  2. Apex Reference Guide: URLClass
  3. stackexchange: How to get salesforce instance url
  4. Salesforce Developer Forum: Download Attachment from Sites??
  5. Salesforce Developer Forum: How to get downloadable link for attachment?

Salesforce 未锁定包(Salesforce Unlocked Package)是Salesforce最新推广的基于包的开发最佳实践。通过Salesforce CLI,我们可以很方便的制作和部署未锁定包。本文只是简略的将核心步骤展示出来:

  1. 创建一个 SFDX 项目

    sfdx force:project:create -d demo-workspace -n expenser-app -p force-app
  2. 授权Dev Hub环境,该环境必须启用Dev Hub功能和未锁定包和第二代管控包功能

    sfdx auth:web:login -d -a devHub
  3. 创建一个草稿环境(scratch org)并在其中开发包

    sfdx force:org:create -f config/project-scratch-def.json -u scratchOrg1
  4. 保证所有要打包的组件都已经在当前的项目文件夹内
  5. 从 SFDX 项目文件夹,直接创建未锁定包:

    sfdx force:package:create -n "Demo App" -r force-app -t Unlocked -o [email protected]
  6. 检查项目文件夹中的sfdx-project.json文件,CLI 会自动更新项目文件,使其包含上一步创建的包的信息:

    {
    "packageDirectories": [
       {
          "path": "force-app",
          "default": true,
          "package": "Demo App",
          "versionName": "ver 0.1",
          "versionNumber": "0.1.0.NEXT"
       }
    ],
    "namespace": "",
    "sfdcLoginUrl": "https://login.salesforce.com",
    "sourceApiVersion": "50.0",
    "packageAliases": {
       "Expense Manager": "0Hoxxx"
    }
    }
  7. 创建一个包的版本,Salesforce CLI会自动处理包的版本号等

    sfdx force:package:version:create --package "Demo App" -k lushang.me --wait 10
  8. 在另一个创建好的草稿环境中,安装并测试这个版本的包:

    sfdx force:package:install -p "Demo [email protected]" -y testOrg1 -k lushang.me --wait 10 --publishwait 10
  9. 如果上一步骤没问题,则可以发布这个版本的包:

    sfdx force:package:version:promote -p "Demo [email protected]"

参考:

  1. Salesforce DX Developer Guide
  2. Workflow for Unlocked Packages
  3. Release an Unlocked Package