【问题标题】:Changing Business Process Flow Stage in C# Plugin在 C# 插件中更改业务流程流阶段
【发布时间】:2018-12-28 01:02:33
【问题描述】:

我正在关注this 文章以更改我在 c# 插件中的业务流程阶段。我能够将舞台向前移动到下一阶段,但是当我尝试回到前一阶段时收到错误消息。下面的错误是我从 Dynamics 在 UI 中收到的。当我调试插件时,我收到一个不包含任何信息的FaultException<OrganizationServiceFault> 异常。为什么我会收到错误消息?如何修改代码以成功返回到业务流程中的上一个阶段?

错误

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: An unexpected error occurred.
Detail: <OrganizationServiceFault xmlns="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <ActivityId>5df51362-b7c1-4817-a8d0-de2d63b15c17</ActivityId>
  <ErrorCode>-2147220970</ErrorCode>
  <ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
  <Message>An unexpected error occurred.</Message>
  <Timestamp>2018-07-19T18:55:42.6625925Z</Timestamp>
  <ExceptionSource i:nil="true" />
  <InnerFault>
    <ActivityId>5df51362-b7c1-4817-a8d0-de2d63b15c17</ActivityId>
    <ErrorCode>-2147220970</ErrorCode>
    <ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
    <Message>System.NullReferenceException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #0D309052</Message>
    <Timestamp>2018-07-19T18:55:42.6625925Z</Timestamp>
    <ExceptionSource i:nil="true" />
    <InnerFault i:nil="true" />
    <OriginalException i:nil="true" />
    <TraceText i:nil="true" />
  </InnerFault>
  <OriginalException i:nil="true" />
  <TraceText i:nil="true" />
</OrganizationServiceFault>

插件

if (localContext == null)
{
    throw new ArgumentNullException("localContext");
}

IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;

Client client = (Client)service.Retrieve(
    Client.LogicalName,
    new Guid("75FE165F-848B-E811-80F3-005056B33317"),
    new ColumnSet(new String[]{
        Client.Properties.ClientId
    })
);

client.ChangeStage(service);

改变阶段

public void ChangeStage(IOrganizationService service)
{
    // Get Process Instances
    RetrieveProcessInstancesRequest processInstanceRequest = new RetrieveProcessInstancesRequest
    {
        EntityId = this.Id,
        EntityLogicalName = this.LogicalName
    };

    RetrieveProcessInstancesResponse processInstanceResponse = (RetrieveProcessInstancesResponse)service.Execute(processInstanceRequest);

    // Declare variables to store values returned in response
    int processCount = processInstanceResponse.Processes.Entities.Count;
    Entity activeProcessInstance = processInstanceResponse.Processes.Entities[0]; // First record is the active process instance
    Guid activeProcessInstanceID = activeProcessInstance.Id; // Id of the active process instance, which will be used later to retrieve the active path of the process instance

    // Retrieve the active stage ID of in the active process instance
    Guid activeStageID = new Guid(activeProcessInstance.Attributes["processstageid"].ToString());

    // Retrieve the process stages in the active path of the current process instance
    RetrieveActivePathRequest pathReq = new RetrieveActivePathRequest
    {
        ProcessInstanceId = activeProcessInstanceID
    };
    RetrieveActivePathResponse pathResp = (RetrieveActivePathResponse)service.Execute(pathReq);

    string activeStageName = "";
    int activeStagePosition = -1;

    Console.WriteLine("\nRetrieved stages in the active path of the process instance:");
    for (int i = 0; i < pathResp.ProcessStages.Entities.Count; i++)
    {
        // Retrieve the active stage name and active stage position based on the activeStageId for the process instance
        if (pathResp.ProcessStages.Entities[i].Attributes["processstageid"].ToString() == activeStageID.ToString())
        {
            activeStageName = pathResp.ProcessStages.Entities[i].Attributes["stagename"].ToString();
            activeStagePosition = i;
        }
    }

    // Retrieve the stage ID of the next stage that you want to set as active
    activeStageID = (Guid)pathResp.ProcessStages.Entities[activeStagePosition - 1].Attributes["processstageid"];

    // Retrieve the process instance record to update its active stage
    ColumnSet cols1 = new ColumnSet();
    cols1.AddColumn("activestageid");
    Entity retrievedProcessInstance = service.Retrieve("ccseq_bpf_clientsetup", activeProcessInstanceID, cols1);

    // Set the next stage as the active stage
    retrievedProcessInstance["activestageid"] = new EntityReference(ProcessStage.LogicalName, activeStageID);
    service.Update(retrievedProcessInstance);
}

更新

我发现 this 文章解释了如何使用 Web API 更新舞台。当我尝试这种方法时,我得到了错误:

未声明的属性“activestageid”仅在负载中具有属性注释,但在负载中未找到属性值。在 OData 中,只有声明的导航属性和声明的命名流可以表示为没有值的属性。

我尝试了几种“activestageid”,但均未成功(ActiveStageId、_activestageid_value)。


更新 2

根据 Arun 的反馈,我尝试了以下 Web API 调用,但均未成功。我从 ccseq_bpf_clientsetups 表上的 BusinessProcessFlowInstanceId 中提取的 url 中括号内的 ID (ccseq_bpf_clientsetups(###))。我从 ProcessStageBase 表中的 ProcessStageId 中提取的 processstages ID

// Attempt 1
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "ActiveStageID@odata.bind": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}

// Attempt 2
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "activestageid@odata.bind": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}

// Attempt 3
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "ActiveStageId@odata.bind": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}

更新 3

我下载了 jLattimer 的 CRM Rest Builder 并尝试运行他的工具生成的 JavaScript。该代码与我之前编写的代码相同,不幸的是没有工作。在这一点上,我相当有信心在 v8.2 的 Web API 中不支持更改阶段。

【问题讨论】:

  • 当前阶段有任何必填字段吗?这可能会阻止您离开活跃阶段
  • @jasonscript 否,当前阶段没有必填字段。我们尝试在完成的 BPF 和不完整的 BPF 上后退一步。在这两种情况下,当前阶段都没有任何必填字段。在 BPF 不完整的情况下,在我们试图退回的上一阶段有一个必填字段,但它确实有一个指定的值

标签: c# plugins dynamics-crm microsoft-dynamics dynamics-crm-2016


【解决方案1】:

我有一些代码试图将业务流程阶段移动向前,作为自定义工作流程步骤(而不是插件)。我已经在下面发布了。

我看到的区别是:

  • 我在前进(而不是后退)
  • 我可能没有遵循最佳做法 :)
  • 我没有检索 active path,我只是获取该过程的所有可用阶段
  • 我也在设置TraversedPath 属性

代码:

var activeInstancesRequest = new RetrieveProcessInstancesRequest
{
    EntityId          = TargetEntity.Id,
    EntityLogicalName = TargetEntity.LogicalName
};
var activeInstancesResponse = (RetrieveProcessInstancesResponse)base.OrgService.Execute(activeInstancesRequest);
var process = activeInstancesResponse.Processes.Entities.Select(x => x.ToEntity<BusinessProcessFlowInstance>()).ToList();
var stages = base.XrmContext.ProcessStageSet
    .Where(s => s.ProcessId.Id == process.FirstOrDefault().ProcessId.Id)
    .Select(s => new ProcessStage
    {
        ProcessStageId = s.ProcessStageId,
        StageName = s.StageName
    })
    .ToList();

var targetStage = stages.Where(stage => stage.StageName == targetStageName).FirstOrDefault();
if (targetStage != null)
{
    crmWorkflowContext.Trace($"BPF contains target stage (\"{targetStageName}\"). Attempting to update BPF");

    // Setting the Traversed Path is necessary for the Business Process Flow to show the active Stage
    // If this is not updated then although the new Stage is set as current, the previous Stage remains actively selected
    var traversedPath = $"{bpf.TraversedPath},{targetStage.ProcessStageId.Value}";
    var update = new BusinessProcessFlowInstance()
    {
        BusinessProcessFlowInstanceId = bpf.BusinessProcessFlowInstanceId,
        ProcessStageId                = targetStage.ProcessStageId,
        TraversedPath                 = traversedPath   
    };

    xrmContext.Attach(update);
    xrmContext.UpdateObject(update);
}

【讨论】:

    【解决方案2】:

    我测试很快,我能够在 vanilla v9 在线组织中的 Lead to Opportunity Sales Process 内前进/后退。

    我只是成功地使用来自 CRM REST 构建器的 Web API PATCH 请求进行了测试。

    发展到提议(转发)

    var entity = {};
    entity["activestageid@odata.bind"] = "/processstages(3A275C22-FC45-4E89-97FC-41E5EC578743)";
    
    var req = new XMLHttpRequest();
    req.open("PATCH", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/leadtoopportunitysalesprocesses(1674DB10-1994-E811-A969-000D3A1A9FA9)", true);
    

    建议开发(向后)

    var entity = {};
    entity["activestageid@odata.bind"] = "/processstages(BFC9108C-8389-406B-9166-2C3298A2E41F)";
    
    var req = new XMLHttpRequest();
    req.open("PATCH", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/leadtoopportunitysalesprocesses(1674DB10-1994-E811-A969-000D3A1A9FA9)", true);
    

    它除了在下面标记为需要之外没有其他任何东西。

    不需要
    1574DB10-1994-E811-A969-000D3A1A9FA9 - leadid
    919E14D1-6489-4852-ABD0-A63A6ECAAC5D - processid
    f99b4d48-7aad-456e-864a-8e7d543f7495,bfc9108c-8389-406b-9166-2c3298a2e41f - traversedpath

    需要
    1674DB10-1994-E811-A969-000D3A1A9FA9 - businessprocessflowinstanceid
    BFC9108C-8389-406B-9166-2C3298A2E41F - activestageid 开发
    3A275C22-FC45-4E89-97FC-41E5EC578743 - activestageid 提议


    更新:

    我也在 v8 中成功测试了以下 sn-p [版本 1612 (8.2.2.2160) (DB 8.2.2.2160) 在线]

    事实上,它正在向后/向前移动没有 traversedpath

    var entity = {};
    entity["activestageid@odata.bind"] = "/processstages(BFC9108C-8389-406B-9166-2C3298A2E41F)";
    entity.traversedpath = "f99b4d48-7aad-456e-864a-8e7d543f7495,bfc9108c-8389-406b-9166-2c3298a2e41f";
    
    var req = new XMLHttpRequest();
    req.open("PATCH", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/leadtoopportunitysalesprocesses(E5B70E69-2094-E811-8145-C4346BDCF2F1)", true);
    

    但得到Bad request 错误,如下:

    entity.activestageid = {
                Id: "3A275C22-FC45-4E89-97FC-41E5EC578743",
                LogicalName: "processstage"
            };
    

    【讨论】:

    • 我目前使用的是 v8,这可能是问题的一部分。另外,根据文档 traversedpath 是必需的,所以不包括它会让我有点紧张
    • @TimHutchison 我验证了 traversedpath 正在向前和向后迅速计算。我还记得我在 v8 中手动执行此操作..
    • @TimHutchison 我在其中一个备份 v8 组织中尝试了来自 CRM Rest builder 的相同请求(+ 遍历路径),但出现错误请求错误。 :(
    • 我在尝试您的解决方案时更新了我的答案。还有其他想法吗?
    • @TimHutchison 忘记你的 BPF,即使 OOB BPF 更改也不起作用。 :) 你试过了吗?那个无用的坏请求不断出现..
    【解决方案3】:

    我遇到了同样的问题。它比你想象的要简单得多。 好的,打开高级查找,选择 {your BPF}。 添加两列进行查询:{your Entity} {traversed path}。

    好的,请查看实际处于前一阶段(您要返回的那个)的实体的遍历路径。

    使用您的代码,您需要动态分解遍历的路径 (.Split(',')) 或类似的东西...删除最后一个阶段(您当前所在的阶段),瞧!你正在用汽油做饭。

    如果当前遍历的路径是一个数组:

    string[] currentPath = {"A", "B", "C", "D"};
    

    您之前的路径必须是:

    string[] previousPath = {"A", "B", "C"};
    

    假设“实体”是您检索到的实体,您可以在代码中执行此操作:

    string traversedPath = (string) entity["traversedpath"];
    string[] stages = traversedPath.Split(',');
    string newPath = "";
    //use length - 1 to omit last stage (your current stage)
    for (int i = 0; i < stages.Length - 1; i++) 
    { 
         if (i != stages.Length - 1)
         newPath += stages[i] + ",";
         else
         newPath += stages[i];
    
    }
    entity["processid"] = new Guid("BPF Guid") //may be optional
    entity["stageid"] = new Guid("previous stage guid");
    entity["traversedpath"] = newPath;
    service.Update(entity);
    

    基本上,遍历路径不会 += 'your previous stage' 到遍历路径的末尾。它想将遍历路径设置为“您的前一个阶段”的原始遍历路径。找出所需阶段的遍历路径,然后将它硬编码成一个字符串(如果它只会进入那个阶段,永远)..或者通过 .Split(',') 方法以编程方式完成Entity["traversedpath"] 代码中的属性。

    请记住,您是从...中减去,而不是添加到...遍历的路径。我花了很多无效的遍历路径错误才得出这个结论……而且它有效。祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-10
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-12
      • 1970-01-01
      相关资源
      最近更新 更多