更改C#插件中的业务流程流程阶段

时间:2018-07-19 19:16:05

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

我正在关注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中提取的网址(ccseq_bpf_clientsetups(###))中括号内的ID。我从ProcessStageBase表中的ProcessStageId中提取的流程阶段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。该代码与我之前编写的代码相同,但不幸的是,该代码无法正常工作。目前,我非常有信心Web API v8.2不支持更改阶段。

3 个答案:

答案 0 :(得分:0)

我有一些代码尝试将业务流程流阶段前进,作为自定义工作流步骤(而不是插件)。我已将其张贴在下面。

我看到的区别是:

  • 我向前(而不是向后)
  • 我可能没有遵循最佳实践:)
  • 我没有检索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);
}

答案 1 :(得分:0)

我进行了快速测试,能够在香草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中成功测试了以下代码段[在线版本<12>版本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"
        };

答案 2 :(得分:0)

我遇到了同样的问题。它比您想象的要简单得多。 确定,打开高级查找,选择{您的BPF}。 添加两列查询:{您的实体} {遍历的路径}。

好,因此,请查看实际处于上一阶段(您想返回的阶段)的实体的遍历路径。

使用您的代码,您需要动态分解遍历的路径(.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);

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

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