這是我的第525篇原創文章,寫於2025年11月26日。
Business Process Flow,簡稱BPF,它的介紹請參考 Business process flows overview 。通過前端代碼與它進行交互請參考 formContext.data.process (Client API reference) 和 formContext.ui.process (Client API reference) 。通過服務器端代碼與它進行交互請參考:https://github.com/microsoft/Dynamics365-Apps-Samples/blob/master/samples-from-msdn/WorkWithBPF/WorkWithBPF.cs 。
如果需要根據狀態的變化,自動跳轉到不同的階段的話,可以先使用 RetrieveProcessInstancesRequest 消息查詢出現在的BPF實例記錄信息,然後再使用 RetrieveActivePathRequest 消息查詢出這個BPF實例的所有Stage,如果找到要設置的Stage的話,就設置當前BPF實例的activestageid 為這個stage就可以了,當然如果這個是最後一個Stage,估計還需要結束這個BPF,那麼就是將這個BPF實例的statecode設置為1,statuscode設置為2。我直接提供一個函數如下:
public static void SetBusinessProcessFlowStage(IOrganizationService service, EntityReference targetEF, string bpfTableLogicalName, string activeStageName,bool isFinishBPF = false)
{
RetrieveProcessInstancesRequest procOpp1Req = new RetrieveProcessInstancesRequest
{
EntityId = targetEF.Id,
EntityLogicalName = targetEF.LogicalName
};
RetrieveProcessInstancesResponse procInstanceResp = (RetrieveProcessInstancesResponse)service.Execute(procOpp1Req);
if (procInstanceResp.Processes.Entities.Any())
{
RetrieveActivePathRequest pathReq = new RetrieveActivePathRequest
{
ProcessInstanceId = procInstanceResp.Processes.Entities[0].Id
};
RetrieveActivePathResponse pathResp = (RetrieveActivePathResponse)service.Execute(pathReq);
var nextActiveStageId = Guid.Empty;
for (int i = 0; i < pathResp.ProcessStages.Entities.Count; i++)
{
if (pathResp.ProcessStages.Entities[i].GetAttributeValue<string>("stagename").Equals(activeStageName, StringComparison.OrdinalIgnoreCase))
{
nextActiveStageId = new Guid(pathResp.ProcessStages.Entities[i].Attributes["processstageid"].ToString());
}
}
if (nextActiveStageId != Guid.Empty)
{
Entity retrievedProcessInstance = service.Retrieve(bpfTableLogicalName, procInstanceResp.Processes.Entities[0].Id, new ColumnSet("activestageid"));
retrievedProcessInstance["activestageid"] = new EntityReference("processstage", nextActiveStageId);
service.Update(retrievedProcessInstance);
if(isFinishBPF)
{
var updateEntity = new Entity(bpfTableLogicalName, procInstanceResp.Processes.Entities[0].Id);
updateEntity["statecode"] = new OptionSetValue(1);
updateEntity["statuscode"] = new OptionSetValue(2);
service.Update(updateEntity);
}
}
}
}
還有碰到的情況就是一個表有多個BPF,如果要切換BPF呢?這就是要設置使用BPF的這個表的 processid 這個字段的值為BPF的Id。那如何找出找個BPF的Id呢?參考如下代碼:
public Entity GetProcessByNameandType(string processName, workflow.category_OptionSet processType, workflow.statuscode_OptionSet statusCode)
{
QueryExpression query = new QueryExpression("workflow");
query.ColumnSet = new ColumnSet("name");
query.Criteria.AddCondition("name", ConditionOperator.Equal, processName);
query.Criteria.AddCondition("category", ConditionOperator.Equal, 4);
query.Criteria.AddCondition("statuscode", ConditionOperator.Equal, 4);
EntityCollection results = orgService.RetrieveMultiple(query);
return results.Entities.FirstOrDefault();
}
找到的這個Entity的Id就是我們要的值,然後參考下面代碼設置就可以:
var updateEntity = new Entity(targetEntity.LogicalName, targetEntity.Id);
updateEntity["processid"] = bpfEntity.Id;
orgService.Update(updateEntity);
但是還有一個問題,如果你切換了BPF後,也要設置這個新的BPF實例的階段怎麼辦?如果你立即或者哪怕等待半分鐘去設置,是不會有用的。因為切換了BPF後,這個新的BPF實例的建立是異步的,立即或者哪怕等待半分鐘去設置階段的時候根本還找不到這個BPF的實例,那設置自然會失敗。怎麼辦?那就是這個BPF實例所在的表註冊一個Create消息的Post階段的異步插件,在插件代碼中調用我前面提供的函數 SetBusinessProcessFlowStage 就可以了。
還有最後一個問題,可能你記錄創建的時候使用的A 這個BPF,後來切換到B這個BPF了,你希望打開表單的時候自動切換到B這個BPF顯示,怎麼辦?這時候用客户端變成來解決,示例代碼如下:
static onLoad(executionContext) {
var formContext = executionContext.getFormContext();
var statusCode = formContext.getAttribute("statuscode").getValue();
if (statusCode === 100000005) {
var activeProcess = formContext.data.process.getActiveProcess();
var activeProcessName = activeProcess.getName();
if (!activeProcessName.includes("BPF B Name")) {
formContext.data.process.getEnabledProcesses(function (processes) {
const processId = Object.keys(processes).find(k => processes[k].includes("BPF B Name"));
if (processId) {
formContext.data.process.setActiveProcess(processId, function (result) {
if (result === "invalid") {
Xrm.Utility.alertDialog("Failed to set process to Abandon.");
}
});
}
});
}
}
}