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

如何基于异步C# OData提升Dynamics CRM的处理吞吐量?

性能优化建议

你当前的POC已经比SSIS快400%,但结合Dynamics CRM OData WebAPI的特性和服务器配置,仍有不少优化空间,针对删除操作给出以下具体建议:

1. 充分利用连接池,调整HttpClient连接限制

你当前GetClient()创建的HttpClient,默认的MaxConnectionsPerServer可能较低(.NET Framework默认是2,.NET Core/5+虽无硬限制,但实际受服务器约束),而服务器允许100个连接,手动调整这个参数能让单个HttpClient利用更多并发连接:

var httpHandler = new HttpClientHandler
{
    Credentials = new NetworkCredential(crmUsername, crmPassword, crmDomain),
    MaxConnectionsPerServer = 100 // 匹配服务器连接限制
};

注意保持HttpClient全局复用(你当前代码已经做到,不要在循环内重复创建)。

2. 提高并行度,匹配服务器连接能力

你当前设置的并行度为4,远低于服务器的100连接上限。可以逐步提高MaximumDegreeOfParallelism的值,先从10-20开始测试,再根据吞吐量调整(建议不超过服务器连接数的80%,避免触发节流):

await Parallel.ForEachAsync(entityList.Chunk(batchMaximumSize ), 
    new ParallelOptions { MaximumDegreeOfParallelism = 15 }, 
    async (chunk, _) =>
{
    // 原batch逻辑不变
});

3. 优化OData批量删除请求

跳过ETag检查

删除操作默认会验证实体的ETag,增加服务器处理开销。可以在Delete请求中添加If-None-Match: *头跳过检查:

batch += oDataClient => oDataClient.For<Entity>()
    .Key(entity.Id)
    .WithHeader("If-None-Match", "*")
    .DeleteEntryAsync(_);

验证Batch请求容量上限

你测试了100-500的batch大小无差异,但可以尝试将batch size提高到1000(Dynamics CRM通常允许单个batch包含最多1000个操作),看是否能提升效率。

4. 使用多服务用户突破单用户并发限制

Dynamics CRM对单个用户的并发请求有默认限制(通常10-20),即使服务器允许100连接,单用户也无法充分利用。你可以创建多个服务用户,为每个用户单独创建HttpClient实例,再分发任务:

// 假设有4个服务用户的凭据列表
var userCredentials = new List<(string Username, string Password)>
{
    ("user1", "pass1"),
    ("user2", "pass2"),
    ("user3", "pass3"),
    ("user4", "pass4")
};

// 为每个用户创建客户端
var clients = userCredentials.Select(cred => GetClient(cred.Username, cred.Password)).ToList();

// 按客户端分发任务
await Parallel.ForEachAsync(entityList.Chunk(batchMaximumSize ), 
    new ParallelOptions { MaximumDegreeOfParallelism = clients.Count * 5 }, 
    async (chunk, _) =>
{
    // 轮询或随机选择客户端
    var client = clients[Random.Shared.Next(clients.Count)];
    var batch = new ODataBatch(client);
    // 后续删除逻辑不变
});

修改GetClient()支持传入用户名密码:

public static IODataClient GetClient(string username, string password)
{
    const string baseAddress = "http://crm-address/";
    const string apiUrl = "api/data/v8.2";
    var crmDomain = Environment.GetEnvironmentVariable("DOMAIN");

    var httpHandler = new HttpClientHandler
    {
        Credentials = new NetworkCredential(username, password, crmDomain),
        MaxConnectionsPerServer = 25 // 每个用户分配25连接,4个用户刚好占满100上限
    };

    var httpClient = new HttpClient(httpHandler)
    {
        BaseAddress = new Uri(baseAddress),
    };

    var odataSettings = new ODataClientSettings(httpClient, new Uri(apiUrl, UriKind.Relative));
    odataSettings.IgnoreResourceNotFoundException = true;

    return new ODataClient(odataSettings);
}

5. 优化实体获取逻辑

如果GetEntitiesForDeletion()返回的是完整实体对象,建议只查询Id字段即可(删除仅需要Id),减少数据传输和内存占用:

// 示例:仅获取删除所需的Id
var entityList = await oDataClient.For<Entity>()
    .Select(e => e.Id)
    .Filter(/* 你的删除条件 */)
    .FindEntriesAsync();

6. 排查服务器端性能瓶颈

  • 检查CRM组织的节流设置:管理员可在CRM后台查看是否有并发请求限制,是否可调整;
  • 禁用非必要插件/触发器:实体的删除前后插件会大幅增加耗时,建议暂时禁用非核心插件测试性能;
  • 确认数据库状态:查看SQL Server的CPU、磁盘IO是否有瓶颈,是否需要优化索引。

关于OData WebAPI的速度限制

OData WebAPI本身确实存在性能上限(对比直接SQL操作),但通过上述优化应该能显著提升吞吐量。如果最终仍无法满足需求,可以考虑改用Organization Service的ExecuteMultipleRequest,它在批量操作上通常比OData WebAPI更高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 09:45:28