如何使用FetchExpression与RetrieveMultiple获取多页实体并批量触发工作流?
解决Dynamics 365中FetchXML分页获取数据并批量触发工作流的问题
一、为FetchXML实现分页循环获取所有数据
FetchXML支持通过<page>和<pagingcookie>节点实现分页,逻辑和QueryExpression的PagingInfo完全对应。你可以通过循环递增页码,利用返回结果的PagingCookie获取下一页数据,直到没有更多记录为止。
核心分页逻辑代码
// 初始化分页参数 int pageNumber = 1; string pagingCookie = null; EntityCollection allEntities = new EntityCollection(); do { // 构建带分页的FetchXML string paginatedFetchXml = ModifyFetchXmlForPaging(FETCH_XML_QUERY, pageNumber, pagingCookie); EntityCollection currentPage = service.RetrieveMultiple(new FetchExpression(paginatedFetchXml)); // 合并当前页数据到总集合 allEntities.Entities.AddRange(currentPage.Entities); Console.WriteLine($"获取第 {pageNumber} 页,共 {currentPage.Entities.Count} 条记录"); // 更新分页参数 pageNumber++; pagingCookie = currentPage.PagingCookie; // 存在分页Cookie且还有更多数据时继续循环 } while (!string.IsNullOrEmpty(pagingCookie) && allEntities.MoreRecords); Console.WriteLine($"累计获取总记录数:{allEntities.Entities.Count}");
辅助修改FetchXML的方法
private static string ModifyFetchXmlForPaging(string originalFetchXml, int pageNumber, string pagingCookie) { var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(originalFetchXml); var fetchNode = xmlDoc.SelectSingleNode("//fetch"); // 设置页码 if (fetchNode.Attributes["page"] != null) { fetchNode.Attributes["page"].Value = pageNumber.ToString(); } else { var pageAttr = xmlDoc.CreateAttribute("page"); pageAttr.Value = pageNumber.ToString(); fetchNode.Attributes.Append(pageAttr); } // 设置分页Cookie(首次请求无需设置) if (fetchNode.Attributes["pagingcookie"] != null) { if (!string.IsNullOrEmpty(pagingCookie)) { fetchNode.Attributes["pagingcookie"].Value = pagingCookie; } else { fetchNode.Attributes.Remove(fetchNode.Attributes["pagingcookie"]); } } else if (!string.IsNullOrEmpty(pagingCookie)) { var cookieAttr = xmlDoc.CreateAttribute("pagingcookie"); cookieAttr.Value = pagingCookie; fetchNode.Attributes.Append(cookieAttr); } // 设置每页最大记录数(上限5000) if (fetchNode.Attributes["count"] != null) { fetchNode.Attributes["count"].Value = "5000"; } else { var countAttr = xmlDoc.CreateAttribute("count"); countAttr.Value = "5000"; fetchNode.Attributes.Append(countAttr); } return xmlDoc.OuterXml; }
二、批量执行工作流提升效率
如果数据量较大,逐个调用ExecuteWorkflowRequest会导致API请求频繁、性能低下,建议使用ExecuteMultipleRequest批量处理,减少请求次数:
if (allEntities.Entities.Count > 0) { Console.WriteLine($"是否运行工作流 \"{WORKFLOW_NAME}\"?输入Y或N"); var key = Console.ReadLine(); if (key.ToLower() == "y") { var executeMultipleRequest = new ExecuteMultipleRequest { Settings = new ExecuteMultipleSettings { ContinueOnError = false, // 出错时是否继续执行后续请求 ReturnResponses = true // 是否返回每个请求的执行结果 }, Requests = new OrganizationRequestCollection() }; // 批量添加工作流执行请求 foreach (var entity in allEntities.Entities) { executeMultipleRequest.Requests.Add(new ExecuteWorkflowRequest { WorkflowId = workflowEntity.Id, EntityId = entity.Id }); } // 执行批量请求 var response = (ExecuteMultipleResponse)service.Execute(executeMultipleRequest); // 统计执行结果 int successCount = 0; int failureCount = 0; foreach (var item in response.Responses) { if (item.Fault != null) { failureCount++; Console.WriteLine($"实体 {item.RequestId} 执行失败:{item.Fault.Message}"); } else { successCount++; } } Console.WriteLine($"工作流执行完成:成功 {successCount} 条,失败 {failureCount} 条"); } }
完整修改后的代码
将以上逻辑整合到原代码中,完整代码如下:
using System; using System.Configuration; using System.Linq; using System.Xml; using Microsoft.Crm.Sdk.Messages; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Client; using Microsoft.Xrm.Sdk.Query; using Microsoft.Xrm.Tooling.Connector; class Program { static void Main(string[] args) { const string WORKFLOW_NAME = "Do the thing"; const string FETCH_XML_QUERY = @"<fetch version=""1.0"" output-format=""xml-platform"" mapping=""logical"" distinct=""false""> <entity name=""new_thing""> <attribute name=""new_thingid"" /> <attribute name=""new_name"" /> <attribute name=""createdon"" /> <order attribute=""new_name"" descending=""false"" /> <filter type=""and""> <condition attribute=""statecode"" operator=""eq"" value=""0"" /> </filter> </entity> </fetch>"; try { var ds = new CRM.CRMService().CreateService() as OrganizationServiceProxy; using (var service = new CrmServiceClient(ds)) { if (service.IsReady) { Console.WriteLine($"已连接到Dynamics 365:{ConfigurationManager.AppSettings["CRM.ServiceUrl"]}"); // 根据名称查询工作流 var query = new QueryExpression("workflow"); query.ColumnSet.AddColumns("workflowid"); query.Criteria.AddCondition("name", ConditionOperator.Equal, WORKFLOW_NAME); Entity workflowEntity = service.RetrieveMultiple(query).Entities.FirstOrDefault(); if (workflowEntity != null) { // 分页获取所有符合条件的实体 int pageNumber = 1; string pagingCookie = null; EntityCollection allEntities = new EntityCollection(); do { string paginatedFetchXml = ModifyFetchXmlForPaging(FETCH_XML_QUERY, pageNumber, pagingCookie); EntityCollection currentPage = service.RetrieveMultiple(new FetchExpression(paginatedFetchXml)); allEntities.Entities.AddRange(currentPage.Entities); Console.WriteLine($"获取第 {pageNumber} 页,共 {currentPage.Entities.Count} 条记录"); pageNumber++; pagingCookie = currentPage.PagingCookie; } while (!string.IsNullOrEmpty(pagingCookie) && allEntities.MoreRecords); Console.WriteLine($"累计获取总记录数:{allEntities.Entities.Count}"); if (allEntities.Entities.Count > 0) { Console.WriteLine($"是否运行工作流 \"{WORKFLOW_NAME}\"?输入Y或N"); var key = Console.ReadLine(); if (key.ToLower() == "y") { // 批量执行工作流 var executeMultipleRequest = new ExecuteMultipleRequest { Settings = new ExecuteMultipleSettings { ContinueOnError = false, ReturnResponses = true }, Requests = new OrganizationRequestCollection() }; foreach (var entity in allEntities.Entities) { executeMultipleRequest.Requests.Add(new ExecuteWorkflowRequest { WorkflowId = workflowEntity.Id, EntityId = entity.Id }); } var response = (ExecuteMultipleResponse)service.Execute(executeMultipleRequest); int successCount = 0; int failureCount = 0; foreach (var item in response.Responses) { if (item.Fault != null) { failureCount++; Console.WriteLine($"实体 {item.RequestId} 执行失败:{item.Fault.Message}"); } else { successCount++; } } Console.WriteLine($"工作流执行完成:成功 {successCount} 条,失败 {failureCount} 条"); } } else { Console.WriteLine("未查询到符合条件的实体"); } } else { Console.WriteLine($"未找到名称为 \"{WORKFLOW_NAME}\" 的工作流"); } } else { Console.WriteLine("连接Dynamics 365失败"); } } } catch (Exception ex) { Console.WriteLine($"错误信息:{ex.Message}"); } Console.WriteLine("按任意键关闭程序"); Console.ReadLine(); } private static string ModifyFetchXmlForPaging(string originalFetchXml, int pageNumber, string pagingCookie) { var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(originalFetchXml); var fetchNode = xmlDoc.SelectSingleNode("//fetch"); // 设置页码 if (fetchNode.Attributes["page"] != null) { fetchNode.Attributes["page"].Value = pageNumber.ToString(); } else { var pageAttr = xmlDoc.CreateAttribute("page"); pageAttr.Value = pageNumber.ToString(); fetchNode.Attributes.Append(pageAttr); } // 设置分页Cookie if (fetchNode.Attributes["pagingcookie"] != null) { if (!string.IsNullOrEmpty(pagingCookie)) { fetchNode.Attributes["pagingcookie"].Value = pagingCookie; } else { fetchNode.Attributes.Remove(fetchNode.Attributes["pagingcookie"]); } } else if (!string.IsNullOrEmpty(pagingCookie)) { var cookieAttr = xmlDoc.CreateAttribute("pagingcookie"); cookieAttr.Value = pagingCookie; fetchNode.Attributes.Append(cookieAttr); } // 设置每页最大记录数 if (fetchNode.Attributes["count"] != null) { fetchNode.Attributes["count"].Value = "5000"; } else { var countAttr = xmlDoc.CreateAttribute("count"); countAttr.Value = "5000"; fetchNode.Attributes.Append(countAttr); } return xmlDoc.OuterXml; } }
注意事项
- 分页Cookie需原样传递,Dynamics 365会通过它标记分页位置,避免记录重复或遗漏
ExecuteMultipleRequest单次最多支持1000个请求,若总记录数超过1000,需拆分多个批量请求- 确保目标工作流的触发方式设置为「手动」或「按需」,否则无法通过代码触发
内容的提问来源于stack exchange,提问作者Ryano
相关产品推荐
相关产品推荐

