Dynamics 365中如何避免OpportunityProduct与lotCommercial并发关联冲突
我正在使用Dynamics 365,在OpportunityProduct实体的PreCreate事件上注册了一个插件。需要防止两名用户同时为同一个名为lotCommercial的自定义实体创建OpportunityProduct,避免并发创建导致重复关联。
起初尝试用versionnumber做并发控制,但因为是PreCreate插件,实体还未创建,无法获取versionnumber。
后来实现了查询现有记录、检测到冲突就抛出异常的逻辑,但两名用户完全同时创建时,两者的验证都通过,最终生成两条记录——这个检查不具备原子性,不可靠。
目标是通过插件或Dynamics 365推荐的机制,简洁可靠地阻止这类竞态条件,该如何实现这类锁定?
tracer.Trace("Vérification de la création simultanée de options..."); QueryExpression query = new QueryExpression("opportunityproduct"); query.ColumnSet.AddColumns("vista_lotcommercialid", "vista_statutoption"); query.Criteria.AddCondition("vista_statutoption", ConditionOperator.Equal, 3); query.Criteria.AddCondition("vista_lotcommercialid", ConditionOperator.Equal, option.GetAttributeValue<EntityReference>("vista_lotcommercialid").Id); //query.Criteria.AddCondition("createdon", ConditionOperator.GreaterEqual, DateTime.UtcNow.AddMilliseconds(-500)); EntityCollection existingOptions = service.RetrieveMultiple(query); tracer.Trace("after retrieve opportunities."); if (existingOptions.Entities.Count > 0) { tracer.Trace("Exception "); throw new InvalidPluginExecutionException("Une option est déjà en cours. Vous ne pouvez pas en créer une nouvelle simultanément."); } tracer.Trace("existingOptions: " + existingOptions.Entities.Count.ToString()); tracer.Trace("End of blocage pose precreateoption s'il y a 2 créations simultanées : " + DateTime.Now.ToLocalTime().ToString("dd/MM/yyyy HH:mm"));
针对Dynamics 365中这种并发创建的竞态条件,推荐以下几种可靠实现方式:
1. 数据库级排他锁(推荐)
通过Retrieve操作的LockingMode参数对关联的lotCommercial记录加排他锁,确保当前事务完成前,其他事务无法修改或创建关联的OpportunityProduct,从根源保证验证与创建的原子性。
修改插件逻辑如下:
// 获取要关联的lotCommercial实体ID Guid lotId = option.GetAttributeValue<EntityReference>("vista_lotcommercialid").Id; // 以排他锁方式检索lotCommercial记录,锁定该记录直至事务结束 Entity lotCommercial = service.Retrieve( "vista_lotcommercial", lotId, new ColumnSet(true), new RetrieveRequest { LockingMode = LockingMode.Exclusive } ); // 执行原有的OpportunityProduct查询验证 QueryExpression query = new QueryExpression("opportunityproduct"); query.ColumnSet.AddColumns("vista_lotcommercialid", "vista_statutoption"); query.Criteria.AddCondition("vista_statutoption", ConditionOperator.Equal, 3); query.Criteria.AddCondition("vista_lotcommercialid", ConditionOperator.Equal, lotId); EntityCollection existingOptions = service.RetrieveMultiple(query); if (existingOptions.Entities.Count > 0) { throw new InvalidPluginExecutionException("已存在关联的有效记录,无法重复创建。"); }
2. 自定义唯一性约束(元数据配置)
如果业务规则是每个lotCommercial只能关联一条状态为3的OpportunityProduct,可以直接在Dynamics 365平台配置唯一约束,无需编写插件:
- 打开对应解决方案,进入OpportunityProduct实体的字段配置界面
- 创建新的唯一约束,添加
vista_lotcommercialid和vista_statutoption两个字段 - 保存并发布解决方案
这种方式由数据库层面强制执行唯一性,是最简洁可靠的方案,仅适用于固定的唯一性规则场景。
3. PostCreate事务回滚验证
若上述两种方式无法适用,可将验证逻辑移至PostCreate事件,结合事务回滚:
- 在PreCreate事件中暂存关联的lotCommercial ID
- 在PostCreate事件中再次查询是否存在重复的OpportunityProduct记录
- 若发现重复,抛出
InvalidPluginExecutionException触发事务回滚,删除已创建的重复记录
注意:这种方式会有短暂的重复记录存在,可靠性略低于前两种方案。
内容的提问来源于stack exchange,提问作者Manel Ben Sayari

