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

如何优化Dynamics CRM查询?请求遇System.TimeoutException超时

Dynamics CRM 查询超时问题优化方案

1. 为过滤/关联字段添加索引

Dynamics CRM对自定义字段默认不创建索引,你的查询里用到了Contact.win_Country和Contact.win_Brand两个自定义字段做过滤,加上关联用的Incident.CustomerId、ActivityPointer.RegardingObjectId,建议检查并添加索引:

  • 登录Dynamics CRM后台,进入高级设置 > 自定义 > 自定义系统 > 实体
  • 找到对应实体(Contact、Incident、Activity Pointer),进入字段,编辑目标字段后勾选对应索引选项(查找字段选「为查找列创建索引」,文本字段选「为文本列创建索引」)
  • 保存并发布变更,索引生效后可大幅提升过滤和关联速度

2. 调整查询关联顺序,先过滤小数据集

当前查询从Activity实体开始关联,会先扫描大量Activity记录再过滤。建议改为从Contact开始,先筛选出符合条件的Contact记录,再关联Incident和Activity,缩小初始数据集范围:

public List<T> GetMarketActivities(win_market market)
{
    var team = (EntityReference)((AliasedValue)market["bu.win_defaultteam"]).Value;

    // 从Contact开始构建查询,先过滤目标数据
    var query = new QueryExpression(Contact.EntityLogicalName);
    query.ColumnSet = new ColumnSet(false); // 不需要Contact字段,仅用于关联
    query.Criteria.AddCondition(new ConditionExpression(Contact.Fields.win_Country, ConditionOperator.Equal, market.win_marketcountry.Id));
    query.Criteria.AddCondition(new ConditionExpression(Contact.Fields.win_Brand, ConditionOperator.Equal, market.win_marketbrand.Id));

    // 关联Incident
    LinkEntity caseLink = new LinkEntity(Contact.EntityLogicalName, Incident.EntityLogicalName, Contact.Fields.ContactId, Incident.Fields.CustomerId, JoinOperator.Inner);
    caseLink.ColumnSet = new ColumnSet(false); // 不需要Incident字段
    query.LinkEntities.Add(caseLink);

    // 关联Activity并过滤OwnerId
    LinkEntity activityLink = new LinkEntity(Incident.EntityLogicalName, _activity.LogicalName, Incident.Fields.IncidentId, ActivityPointer.Fields.RegardingObjectId, JoinOperator.Inner);
    activityLink.ColumnSet = new ColumnSet(ActivityPointer.Fields.ActivityId, ActivityPointer.Fields.OwnerId);
    activityLink.LinkCriteria.AddCondition(new ConditionExpression(ActivityPointer.Fields.OwnerId, ConditionOperator.NotEqual, team.Id));
    activityLink.TopCount = 100;
    caseLink.LinkEntities.Add(activityLink);

    var results = _organizationService.RetrieveMultiple(query);

    // 提取关联的Activity实体
    if (results.Entities?.Count > 0)
    {
        return results.Entities
            .Select(e => e.GetRelatedEntity<T>(activityLink.EntityAlias))
            .Where(a => a != null)
            .ToList();
    }

    return new List<T>();
}

3. 限制关联实体的返回字段集

原查询未限制关联实体的返回字段,即使不需要也会返回默认字段,增加数据传输量。修改LinkEntity时指定仅返回关联所需字段:

// 原caseLink修改
LinkEntity caseLink = new LinkEntity(_activity.LogicalName, Incident.EntityLogicalName, 
    ActivityPointer.Fields.RegardingObjectId, Incident.Fields.IncidentId, JoinOperator.Inner);
caseLink.ColumnSet = new ColumnSet(Incident.Fields.IncidentId, Incident.Fields.CustomerId); // 仅保留关联用字段
query.LinkEntities.Add(caseLink);

// 原contactLink修改
LinkEntity contactLink = new LinkEntity(Incident.EntityLogicalName, Contact.EntityLogicalName, 
    Incident.Fields.CustomerId, Contact.Fields.ContactId, JoinOperator.Inner);
contactLink.ColumnSet = new ColumnSet(Contact.Fields.ContactId); // 仅保留关联用字段
contactLink.LinkCriteria.AddCondition(new ConditionExpression(Contact.Fields.win_Country, ConditionOperator.Equal, market.win_marketcountry.Id));
contactLink.LinkCriteria.AddCondition(new ConditionExpression(Contact.Fields.win_Brand, ConditionOperator.Equal, market.win_marketbrand.Id));
caseLink.LinkEntities.Add(contactLink);

4. 启用分页查询

即使设置了TopCount,单次查询仍可能因数据集过大超时,改用分页分批次获取数据:

public List<T> GetMarketActivities(win_market market)
{
    var team = (EntityReference)((AliasedValue)market["bu.win_defaultteam"]).Value;
    List<T> activities = new List<T>();
    int pageNumber = 1;
    string pagingCookie = null;

    do
    {
        var query = new QueryExpression(_activity.LogicalName);
        query.TopCount = 50; // 每页取50条
        query.ColumnSet = new ColumnSet(ActivityPointer.Fields.ActivityId, ActivityPointer.Fields.OwnerId);
        query.Criteria.AddCondition(new ConditionExpression(ActivityPointer.Fields.OwnerId, ConditionOperator.NotEqual, team.Id));
        
        // 关联设置同原代码(需配合步骤3的字段限制)
        LinkEntity caseLink = new LinkEntity(_activity.LogicalName, Incident.EntityLogicalName, 
            ActivityPointer.Fields.RegardingObjectId, Incident.Fields.IncidentId, JoinOperator.Inner);
        caseLink.ColumnSet = new ColumnSet(Incident.Fields.IncidentId, Incident.Fields.CustomerId);
        query.LinkEntities.Add(caseLink);

        LinkEntity contactLink = new LinkEntity(Incident.EntityLogicalName, Contact.EntityLogicalName, 
            Incident.Fields.CustomerId, Contact.Fields.ContactId, JoinOperator.Inner);
        contactLink.ColumnSet = new ColumnSet(Contact.Fields.ContactId);
        contactLink.LinkCriteria.AddCondition(new ConditionExpression(Contact.Fields.win_Country, ConditionOperator.Equal, market.win_marketcountry.Id));
        contactLink.LinkCriteria.AddCondition(new ConditionExpression(Contact.Fields.win_Brand, ConditionOperator.Equal, market.win_marketbrand.Id));
        caseLink.LinkEntities.Add(contactLink);

        // 设置分页参数
        query.PageInfo = new PagingInfo
        {
            PageNumber = pageNumber,
            PagingCookie = pagingCookie,
            Count = query.TopCount.Value
        };

        var result = _organizationService.RetrieveMultiple(query);
        if (result.Entities?.Count > 0)
        {
            activities.AddRange(result.Entities.Select(e => e.ToEntity<T>()));
        }

        // 更新分页状态
        pagingCookie = result.PagingCookie;
        pageNumber++;
    } while (!string.IsNullOrEmpty(pagingCookie) && activities.Count < 1000); // 最多取1000条

    return activities;
}

5. 替换为FetchXML(可选)

部分复杂关联场景下,FetchXML的查询优化表现更优,可尝试转换为以下FetchXML执行:

<fetch top="100" >
  <entity name="activitypointer" >
    <attribute name="activityid" />
    <attribute name="ownerid" />
    <filter type="and" >
      <condition attribute="ownerid" operator="neq" value="{TEAM_ID}" />
    </filter>
    <link-entity name="incident" from="incidentid" to="regardingobjectid" link-type="inner" >
      <link-entity name="contact" from="contactid" to="customerid" link-type="inner" >
        <filter type="and" >
          <condition attribute="win_country" operator="eq" value="{COUNTRY_ID}" />
          <condition attribute="win_brand" operator="eq" value="{BRAND_ID}" />
        </filter>
      </link-entity>
    </link-entity>
  </entity>
</fetch>

代码中调用方式:

var fetchXmlTemplate = "<fetch top=\"100\" >...</fetch>"; // 替换为上述完整FetchXML
var fetchXml = string.Format(fetchXmlTemplate, team.Id, market.win_marketcountry.Id, market.win_marketbrand.Id);
var results = _organizationService.RetrieveMultiple(new FetchExpression(fetchXml));
// 后续实体转换逻辑同原代码

内容的提问来源于stack exchange,提问作者jbm_force_tranquille

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:54:54