Salesforce触发无限循环

时间:2012-07-20 20:47:40

标签: triggers salesforce apex-code infinite-loop

首先,我们有一个产品可以通过api与salesforce同步数据。当我们的平台中更新记录时,数据会被推送到salesforce,当数据在salesforce中更新时,它会被推送到我们的产品中。我们遇到的问题是无限循环,这是一个相当麻烦的问题。 SalesForce中是否有一种方法可以确定谁在执行触发器,以便在我们的产品中更新数据并将其推送到salesforce时,触发器不会触发?我之前没有看过关于此的执行信息所以我真的不确定。

谢谢!

5 个答案:

答案 0 :(得分:4)

我不相信除了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;
                  }
            }
      }
}

答案 1 :(得分:1)

对于在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();,以防止触发器在其余方法中触发。

答案 2 :(得分:0)

我同意Adam关于如何更好地确定何时执行同步。您还可以考虑使用UserInfo.getUserId()方法查看当前正在更新记录的用户,然后查找用户。

我想,为了让您的集成工作,您必须将用户分配给外部产品。希望这是他们独有的帐户,这是最好的做法。如果是这样,那么您可以查看当前用户是谁,如果是外部产品,您可以忽略尝试发送另一个更新。

答案 3 :(得分:0)

对于要过滤掉逻辑的每个“免除源”,在类型为Number(18,0)的目标对象上创建一个字段。我通常称这个字段为“[Job]最后更新”。该字段不应位于任何面向用户的页面布局上。

然后,每当您的免除源执行更新时,您将该字段设置为System.currentTimeMillis()。您的触发器会检查该字段是否已更改,如果已更改,则会免除您希望省略该源的逻辑。

亚当提到的解决方案 - 保留最新更新的来源 - 也会有效但我发现如果你不总是正确地更新它或检查它是否已经改变,它可能容易出错。这就是为什么我更喜欢在您想要逻辑豁免时更改的字段,并且您只需要为那些免除更改源更新它。

答案 4 :(得分:0)

如果您使用专用用户进行产品中的API调用,则可以为其禁用Send Outbound Messages用户权限。这不会阻止某些触发器代码运行,但它会阻止每次开发时的任何更新周期。