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



我遵循这篇文章,在c#插件中更改我的业务流程阶段。我可以将舞台向前移动到下一个舞台,但当我试图移回前一个舞台时,我收到了一个错误。下面的错误是我在UI中从Dynamics收到的错误。当我调试插件时,我收到一个不包含任何信息的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);
}

更新

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

未声明的属性"activestageid",该属性在负载中只有属性注释,但在负载中未找到属性值。在OData中,只有已声明的导航属性和已声明的命名流才能表示为不带值的属性。

我尝试了几种"activestageid",但没有成功(activestageid,_activestageid_value)。


更新2

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

// 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不支持更改阶段。

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

我看到的区别是:

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

我测试得很快,能够在vanillav9在线组织中的Lead to Opportunity Sales Process中向前/向后移动。

我简单地用Web APIPATCH请求成功地测试了来自CRM REST构建器的请求。

开发以提出(转发)

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-A69-000D3A1A9FA9-leadid
919E14D1-6489-4852-ABD0-A63A6ECAAC5D-processid
f99b4d48-7aad-456e-864a-8e7d543f7495,bfc9108c-8389-406b-9166-2c3298a2e41f-traversedpath

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


更新:

我已经在v8中成功地测试了下面的代码段[Version 1612(8.2.2.2160)(DB 8.2.2.22160)online]

事实上,它在没有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"
};

我也遇到了同样的问题。这比你想象的要简单得多。好的,打开高级查找,选择{您的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);

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

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

最新更新