CRM 2016 On-Premise插件仅更新fieldA未触发fieldB更新排查
问题根因
你遇到的问题核心出在两个位置:
- 代码执行到
fieldB = account.Attributes["new_fieldB"].ToString();行时直接抛出运行时异常,中断了执行流,导致该行之后的异常抛出、字段赋值逻辑完全无法运行。
出现这个异常的常见原因有两个:- 注册PostImage时漏选了
new_fieldB字段:PostImage的返回字段是独立于插件步骤筛选属性单独配置的,如果你没在PostImage的配置项里勾选new_fieldB,PostImage本身就不携带这个字段;加上你用的自定义GenerateCompositeEntity合并方法没有处理null值/缺失属性的场景,当Target中new_fieldB值为null时,合并后的account实体Attributes集合里根本不存在new_fieldB,直接用索引器取值就会抛出KeyNotFoundException。 - 字段类型转换错误:如果
new_fieldB不是单行文本字段(是选项集、查找、货币、时间等类型),直接对属性值调用ToString()会抛出类型转换异常。
- 注册PostImage时漏选了
- 你在Post-Operation阶段手动调用
service.Update(targetEntity)是错误写法:同步Post-Operation阶段仍处于当前更新操作的数据库事务上下文内,直接修改InputParameters里的Target属性,平台会自动把变更持久化到数据库,不需要手动调用Update。手动调用会触发嵌套的插件执行链,被你写的context.Depth == 1判断拦截,反而会导致更新逻辑不执行,甚至触发死循环。
修复方案
- 先检查插件注册配置
打开插件注册工具,找到对应插件步骤下的PostImage配置,确认new_fieldA、new_fieldB两个字段都已经被勾选为PostImage的返回字段。 - 替换不安全的取值逻辑
不要直接用索引器+ToString()取值,改用SDK自带的GetAttributeValue<T>方法做安全取值,避免缺失属性、null值、类型不匹配导致的运行时异常,示例修复代码如下:
string fieldA; string fieldB; var targetEntity = context.GetParameterCollection<Entity>(context.InputParameters, "Target"); if (targetEntity == null) throw new InvalidPluginExecutionException(OperationStatus.Failed, "Target Entity cannot be null"); var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); var service = serviceFactory.CreateOrganizationService(context.UserId); if (context.Depth == 1) { var postImage = context.PostEntityImages["PostImage"]; if (postImage == null) throw new InvalidPluginExecutionException(OperationStatus.Failed, "Post Image is required"); var account = context.GenerateCompositeEntity(targetEntity, postImage); if (targetEntity.Attributes.Contains("new_fieldA")) { // 安全取字符串值,字段不存在/值为null时默认返回null fieldA = account.GetAttributeValue<string>("new_fieldA"); // 加密前先判空,避免加密方法报错 if (!string.IsNullOrEmpty(fieldA)) { targetEntity["new_fieldA"] = encrypt(fieldA); } } if (targetEntity.Attributes.Contains("new_fieldB")) { // 安全取值 fieldB = account.GetAttributeValue<string>("new_fieldB"); // 写Trace日志排查问题,不需要靠throw测试执行流 context.Trace("处理fieldB,取到的值为:{0}", fieldB); if (!string.IsNullOrEmpty(fieldB)) { targetEntity["new_fieldB"] = encrypt(fieldB); } } // 删除多余的service.Update调用,平台会自动持久化对Target的修改 }
- 优化插件注册阶段
做字段值加密、格式修正这类逻辑,最佳注册阶段是同步Pre-Operation,这个阶段修改Target完全不需要手动调用Update就能持久化,不会触发嵌套插件执行,事务一致性也更好。如果你的插件当前注册的是异步Post-Operation步骤,那么修改Target不会自动持久化,必须调整注册阶段。 - 开启插件跟踪日志排查隐藏异常
如果你改完逻辑还是有问题,可以在系统设置里开启插件跟踪,把执行过程中的变量状态、报错信息写到Trace里,不用靠手动抛异常判断代码执行位置。
内容的提问来源于stack exchange,提问作者ichachan
相关产品推荐
相关产品推荐

