首先,我们有一个产品可以通过 api 与 Salesforce 同步数据。当我们平台中的记录更新时,数据被推送到salesforce,当数据在salesforce中更新时,它被推送到我们的产品。我们遇到的问题是一个相当麻烦的无限循环。SalesForce 中是否有办法确定谁在执行触发器,以便在我们的产品中更新数据并将其推送到 salesforce 时,触发器不会触发?我之前没有看到有关此的执行信息,所以我真的不确定。
谢谢!
首先,我们有一个产品可以通过 api 与 Salesforce 同步数据。当我们平台中的记录更新时,数据被推送到salesforce,当数据在salesforce中更新时,它被推送到我们的产品。我们遇到的问题是一个相当麻烦的无限循环。SalesForce 中是否有办法确定谁在执行触发器,以便在我们的产品中更新数据并将其推送到 salesforce 时,触发器不会触发?我之前没有看到有关此的执行信息,所以我真的不确定。
谢谢!
我不相信除了 Trigger.isExecuting 之外还有什么,它只是告诉您是否已经在触发器上下文中,或者您是否来自 Visualforce、API 或匿名执行。
一种可能的解决方案是创建一个保留最新更新来源的自定义字段。然后可以使用它在触发器中正确路由逻辑,并避免在最后一次更新来自外部系统时调用。此外,您可以遍历 Trigger.old 和 trigger.new 中的所有字段,以查看哪些字段发生了变化。这样,当您的记录(SystemTimeStamp、LastModifiedDate 等)上没有任何有趣的实际变化时,您可以避免进一步不必要的与外部系统同步。以下是如何执行此操作的示例:
trigger ContactDescribeExample on Contact (before update)
{
// Get describe fields to evaluate old and new triggers with
Map<String, Schema.SObjectField> fldObjMap = Schema.SObjectType.Contact.fields.getMap();
List<Schema.SObjectField> fldObjMapValues = fldObjMap.values();
// Flag to determine if field(s) other than FirstName caused this change (FirstName is just an example)
Boolean hasOtherChange = false;
// Loop through trigger batch
for(Contact c : Trigger.new)
{
for(Schema.SObjectField s : fldObjMapValues)
{
String fldName = s.getDescribe().getName();
// Filter out fields we're not interested in
if(fldName != 'FirstName' && fldName != 'LastModifiedDate' && fldName != 'LastModifiedById' && fldName != 'SystemModstamp')
{
// Check to see if old and new are different
if(c.get(fldName) != Trigger.oldMap.get(c.Id).get(fldName))
hasOtherChange = true;
}
}
}
}
对于在 Google 上发现此问题的任何人,我通过添加一个设置触发器然后检查的公共静态变量的类来解决一个非常相似的问题。默认为 false,如果设置为 true,则触发器不会运行。下面的例子:
public class Recursive {
// Static variables to avoid recursion on trigger operation
private static boolean working = false;
public static boolean isWorking() {
return working;
}
// Set variable to true to mark record as working
public static void setWorking() {
working = true;
}
public static void setClosed() {
working = false;
}
}
然后触发器被包装在以下内容中:
if (!Recursive.isWorking()) {
// trigger code here
}
作为一个静态变量,它在整个代码执行过程中保持设置,所以我的顶点设置的第一件事是Recursive.setWorking();
防止触发器在其余方法中触发。
如果您为从您的产品传入的 API 调用使用专用用户,那么您可以为他们禁用发送出站消息用户权限。这不会阻止某些触发代码运行,但会阻止每次开发的任何更新周期。
对于您希望过滤掉逻辑的每个“豁免源”,您在 Number(18,0) 类型的目标对象上创建一个字段。我通常将此字段称为“[作业] 上次更新”。该字段不应位于任何面向用户的页面布局上。
然后,每当您的豁免源执行更新时,您将该字段设置为 System.currentTimeMillis()。您的触发器检查该字段是否已更改,如果已更改,则免除您希望为该源省略的逻辑。
Adam 提到的解决方案 - 保留最新更新的来源 - 也可以工作,但我发现如果您不总是正确更新它或检查它是否已更改,它可能容易出错。这就是为什么我更喜欢在您需要逻辑豁免时更改的字段,并且您只需针对那些豁免的更改来源对其进行更新。
我同意 Adam 关于如何更好地确定何时执行同步的观点。您还可以考虑使用 UserInfo.getUserId() 方法查看当前正在更新记录的用户,然后查找该用户。
我想,为了让您的集成工作,您必须为外部产品分配一个用户。希望这是他们独有的帐户,这将是最佳实践。如果是这样,那么您只需查看当前用户是谁,如果是该外部产品,您可以忽略尝试发送另一个更新。