Dynamics CRM批量更新事务批量大小问题及关联联系人创建保障问询
问题描述
需求
- 从API读取数据,更新Account(账户)的部分字段,并为其创建对应的Contact(联系人)记录。
- 需处理约10万条记录,计划使用
ExecuteTransactionRequest或ExecuteMultipleRequest批量执行;因需确保账户更新后同步创建联系人,故选用ExecuteTransactionRequest。
当前问题
当前设置当请求批次数量达到500时执行事务,但批次中同时包含Account更新请求和Contact创建请求,可能出现批次凑不满500条而未执行的情况,如何确保每个更新后的Account都能正确创建对应的Contact?
现有代码
var requests = new ExecuteTransactionRequest { Requests = new OrganizationRequestCollection(), ReturnResponses = returnResponses }; foreach (var customer in customerList) { string custNo = customer.GetAttributeValue<string>("customernumber"); // 从其他API获取客户详情 var custInfo = await CustomerService.Get(custNo); // 更新客户表字段 Entity cust = new Entity("account"); cust.Id = customer.Id; cust["companytypecode"] = custInfo.EntityTypeCode; cust["companytypedescription"] = custInfo.EntityTypeDescription; var roles = custInfo.Roles.Where(c => c.RoleStatus == "ACTIVE").ToArray(); // 为每个账户创建联系人 foreach(var role in roles) { Entity contact = new Entity("contact"); contact["FirstName"] = role.RolePerson?.FirstName; contact["MiddleName"] = role.RolePerson?.MiddleNames; contact["LastName"] = role.RolePerson?.LastName; contact["AccountId"] = new EntityReference("account", customer.Id); CreateRequest createRequest = new CreateRequest { Target = contact }; requests.Requests.Add(createRequest); } UpdateRequest updateRequest = new UpdateRequest { Target = cust }; requests.Requests.Add(updateRequest); if (requests.Requests.Count == 500) // 问题:因为包含联系人创建请求,批次不会按单个账户执行,如何确保每个请求都正确执行? { service.Execute(requests); requests.Requests.Clear(); } } // 处理剩余的账户请求 if (requests.Requests.Count > 0) { service.Execute(requests); }
解决方案
核心思路是把单个Account的更新和其对应的所有Contact创建请求作为一组原子操作,同时控制每个ExecuteTransactionRequest的总请求数不超过Dynamics 365默认的500上限,既保证每个Account操作的完整性,又兼顾批量执行的性能。
具体实现步骤
- 按Account分组处理请求:每个Account对应的更新请求+所有关联联系人的创建请求为一组,确保这组操作要么全部成功,要么全部回滚。
- 动态管控批次容量:每次添加一组请求前,检查当前批次总请求数是否会超过上限,若超过则先执行现有批次再添加新组。
- 收尾剩余请求:循环结束后执行所有未提交的剩余请求。
修改后的代码
// 单批次最大请求数(遵循Dynamics 365限制) const int maxBatchRequestCount = 500; var currentTransactionBatch = new ExecuteTransactionRequest { Requests = new OrganizationRequestCollection(), ReturnResponses = returnResponses }; foreach (var customer in customerList) { string custNo = customer.GetAttributeValue<string>("customernumber"); var custInfo = await CustomerService.Get(custNo); // 构建Account更新请求 var accountUpdateReq = new UpdateRequest { Target = new Entity("account") { Id = customer.Id, ["companytypecode"] = custInfo.EntityTypeCode, ["companytypedescription"] = custInfo.EntityTypeDescription } }; // 构建该Account对应的所有Contact创建请求 var contactCreateReqs = custInfo.Roles .Where(r => r.RoleStatus == "ACTIVE") .Select(role => new CreateRequest { Target = new Entity("contact") { ["FirstName"] = role.RolePerson?.FirstName, ["MiddleName"] = role.RolePerson?.MiddleNames, ["LastName"] = role.RolePerson?.LastName, ["AccountId"] = new EntityReference("account", customer.Id) } }) .ToList(); // 计算当前Account组的总请求数 int currentGroupCount = 1 + contactCreateReqs.Count; // 如果当前批次加新组会超限,先执行现有批次 if (currentTransactionBatch.Requests.Count + currentGroupCount > maxBatchRequestCount) { service.Execute(currentTransactionBatch); currentTransactionBatch.Requests.Clear(); } // 将当前Account的所有请求加入批次 currentTransactionBatch.Requests.Add(accountUpdateReq); currentTransactionBatch.Requests.AddRange(contactCreateReqs); } // 执行最后剩余的请求批次 if (currentTransactionBatch.Requests.Count > 0) { service.Execute(currentTransactionBatch); }
关键说明
- 原子性保障:单个Account的更新和联系人创建请求连续加入批次,
ExecuteTransactionRequest会按顺序执行所有请求,一旦某一步失败,整个批次会回滚。如果需要更严格的单Account独立事务,可以为每个Account单独创建ExecuteTransactionRequest,但会增加API调用次数,需在性能与原子性间权衡。 - 容量管控:提前计算每组请求数量,避免批次超限导致执行失败,同时尽可能让批次接近500请求上限,减少API调用次数,提升10万条记录的处理效率。
- 兼容性:完全贴合Dynamics 365的批量请求限制,不会出现因请求数超限导致的异常。
内容的提问来源于stack exchange,提问作者MVC_Nhibernate
相关产品推荐
相关产品推荐

