You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 15:47:13