Dynamics 365销售订单明细插件报错0x80040265求助
问题现象
开发的插件在**销售订单明细(salesorderdetail)**实体创建事件触发时,出现实体锁定错误,插件跟踪日志如下:
get Ref
productIdRef
de_prdrate: 19
de_salesprdrate: 19System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: The product and the unit cannot be updated while the entity is locked. (Fault Detail is equal to Exception details:
ErrorCode: 0x80040265
Message: The product and the unit cannot be updated while the entity is locked.
OriginalException: PluginExecution ExceptionSource: PluginExecution
补充信息:销售订单明细的创建事件同时有系统工作流在运行,该插件在**报价单明细(quotedetail)**实体上运行无异常,怀疑是插件与系统工作流触发同一事件导致冲突。
插件代码
public class Update_Salesdetails_Field : IPlugin { public void Execute(IServiceProvider serviceProvider) { ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { Entity entity = (Entity)context.InputParameters["Target"]; IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); try { if (entity.LogicalName != "salesorderdetail") { tracingService.Trace("Target entity is not Sales Order Detail! plug-in was not registered correctly! Exit PlugIn", serviceProvider); throw new InvalidPluginExecutionException("The current entity is not the Sales Order Detail entity"); } tracingService.Trace("get Ref", serviceProvider); var productIdRef = entity.GetAttributeValue<EntityReference>("productid"); tracingService.Trace("productIdRef", serviceProvider); var productEntity = service.Retrieve("product", productIdRef.Id, new ColumnSet("de_prdrate")); tracingService.Trace("de_prdrate: " + productEntity["de_prdrate"]); entity["de_salesprdrate"] = productEntity["de_prdrate"]; tracingService.Trace("de_salesprdrate " + entity["de_salesprdrate"]); service.Update(entity); tracingService.Trace("Update salesprdrate " + entity["de_salesprdrate"]); } catch (FaultException<OrganizationServiceFault> ex) { tracingService.Trace("exception:", ex.ToString()); tracingService.Trace(ex.StackTrace); if (ex.InnerException != null) { tracingService.Trace("inner exception:", ex.InnerException.ToString()); tracingService.Trace(ex.InnerException.StackTrace); } throw new InvalidPluginExecutionException(ex.Message); } catch (Exception ex) { tracingService.Trace("FollowUpPlugin: {0}", ex.ToString()); tracingService.Trace(ex.StackTrace); throw; } } } }
解决方案建议
1. 切换到Pre-Create阶段执行(最优方案)
当前插件应该是注册在**Post-Create(创建后)**阶段,此时调用service.Update会触发额外的更新请求,而系统工作流可能已经锁定了实体(尤其是涉及产品、单位字段的操作)。
将插件注册到**Pre-Create(创建前)**阶段,直接在Target实体上设置de_salesprdrate字段值即可,无需调用service.Update——Pre阶段对Target的修改会直接被包含在创建请求中,彻底避免实体锁定冲突。
修改后的核心逻辑:
// 移除原代码中的service.Update(entity); // 直接赋值,Pre阶段自动带入创建操作 entity["de_salesprdrate"] = productEntity["de_prdrate"]; tracingService.Trace("Set de_salesprdrate to " + entity["de_salesprdrate"]);
2. 调整插件执行顺序
如果必须在Post-Create阶段执行,调整插件的执行顺序,让它在系统工作流之后运行。这样系统工作流完成字段处理并释放锁定后,插件再执行更新操作。
3. 添加重试逻辑处理短暂锁定
在更新操作外包裹重试逻辑,应对短暂的实体锁定情况:
int retryCount = 3; while (retryCount > 0) { try { service.Update(entity); break; } catch (FaultException<OrganizationServiceFault> ex) when (ex.Detail.ErrorCode == 0x80040265) { retryCount--; if (retryCount == 0) throw; System.Threading.Thread.Sleep(1000); // 等待1秒后重试 } }
4. 排查系统工作流的操作
查看系统工作流对销售订单明细的具体操作:
- 确认是否有长时间锁定实体的逻辑
- 检查工作流是否也修改了
de_salesprdrate字段,导致冲突 - 若存在字段操作冲突,可以将逻辑合并到插件或工作流中,避免重复操作
内容的提问来源于stack exchange,提问作者Alloylo

