Dynamics CRM插件:新建Incident记录传值外部服务及字段更新问题
问题1:新建记录时无法正确获取字段值
你遇到的第一个问题,本质是插件执行阶段和字段类型处理的问题,咱们一步步拆解:
为什么新建时直接用entity["X"]可能拿不到值?
当插件注册在Create消息的PreOperation阶段时,Target实体里只包含用户在表单上主动填写的字段,像createdon、ticketnumber这类系统自动生成的字段,还没写入数据库,所以直接从Target里拿不到。如果业务必须用到这些系统字段,要么把插件注册到PostOperation阶段,要么换个思路调整逻辑(不推荐Pre阶段先创建再查询,会多一次数据库操作)。
关于你碰到的Retrieve报错:Unable to cast object of type Microsoft.Xrm.Sdk.OptionSetValue to type Microsoft.Xrm.Sdk.EntityReference
这个错误很明确:你把一个**选项集字段(OptionSetValue)当成实体引用字段(EntityReference)**强转了。看你的代码,你写的是Owner = wholeCase.GetAttributeValue<EntityReference>("customerid").ToString();,但customerid是客户的实体引用,理论上不该是OptionSet。大概率是你写错了字段名(比如把某个状态字段误写成customerid),或者在ColumnSet里包含了其他OptionSet字段,却用EntityReference去获取。
不同类型字段的正确获取方式:
- 文本/字符串字段:
entity.GetAttributeValue<string>("subject") - 实体引用字段(比如customerid、ownerid):
var customerRef = wholeCase.GetAttributeValue<EntityReference>("customerid"); string owner = customerRef != null ? $"{customerRef.Name} ({customerRef.Id})" : string.Empty; - 日期时间字段(比如createdon):
DateTime createTime = wholeCase.GetAttributeValue<DateTime>("createdon"); string createTimeStr = createTime.ToString("yyyy-MM-dd HH:mm:ss"); // 转成你需要的格式 - 选项集字段(比如statecode、statuscode):
var stateOption = wholeCase.GetAttributeValue<OptionSetValue>("statecode"); string states = stateOption != null ? stateOption.Value.ToString() : string.Empty; // 如果要获取选项集的显示名称,需要查询全局选项集或实体元数据
另外,新建记录时如果用PostOperation阶段,recordID = entity.Id是有效的,可以直接用这个ID去Retrieve完整实体。
问题2:更新记录时无法设置字段值
你说更新时用entity["X"] = "NewValue"不生效,核心要看插件注册的阶段:
- 如果是PreOperation阶段:直接修改
Target实体的属性是有效的,比如:
但要确保字段名拼写正确,且该字段具备可写权限。entity["new_customfield"] = res.ToString(); - 如果是PostOperation阶段:
Target实体是只读的,这时候需要创建UpdateRequest来更新记录:Entity updateEntity = new Entity("incident", entity.Id); updateEntity["new_customfield"] = res.ToString(); service.Update(updateEntity);
补充:如果插件注册在Create消息的Post阶段,要设置字段值的话,同样需要用UpdateRequest——因为此时Target已经是数据库里的记录,直接修改entity不会同步到数据库。
修正后的完整代码片段参考
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService service = factory.CreateOrganizationService(context.UserId); if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { try { var entity = (Entity)context.InputParameters["Target"]; if (entity.LogicalName != "incident") return; TicketViewModel ticket = new TicketViewModel(); // 根据插件阶段处理字段获取 if (context.MessageName == "Create" && context.Stage == 40) // PostOperation阶段 { Guid recordID = entity.Id; ColumnSet cs = new ColumnSet("ticketnumber", "title", "description", "createdon", "customerid", "new_peygiriii", "createdby"); Entity wholeCase = service.Retrieve("incident", recordID, cs); // 正确获取各字段值 ticket.CaseID = wholeCase.GetAttributeValue<string>("ticketnumber"); ticket.Subject = wholeCase.GetAttributeValue<string>("title"); ticket.Description = wholeCase.GetAttributeValue<string>("description"); DateTime createOn = wholeCase.GetAttributeValue<DateTime>("createdon"); ticket.CreateTime = createOn.ToString("yyyy-MM-dd HH:mm:ss"); var customerRef = wholeCase.GetAttributeValue<EntityReference>("customerid"); ticket.Owner = customerRef != null ? customerRef.Name : string.Empty; var stateOption = wholeCase.GetAttributeValue<OptionSetValue>("statecode"); ticket.States = stateOption != null ? stateOption.Value.ToString() : string.Empty; var assignedRef = wholeCase.GetAttributeValue<EntityReference>("createdby"); ticket.Assigned = assignedRef != null ? assignedRef.Name : string.Empty; } else if (context.MessageName == "Create" && context.Stage == 20) // PreOperation阶段,只能拿用户填写的字段 { ticket.Subject = entity.GetAttributeValue<string>("title"); ticket.Description = entity.GetAttributeValue<string>("description"); // 系统字段拿不到,需要调整业务逻辑或者换阶段 } // 调用Web服务 BasicHttpBinding httpBinding = new BasicHttpBinding(); httpBinding.Name = "HttpBinding_Service"; httpBinding.Security.Mode = BasicHttpSecurityMode.None; httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; httpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None; httpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; EndpointAddress epa = new EndpointAddress(@"webservice/url/address"); CallChamberPortalSoapClient tcClient = new CallChamberPortalSoapClient(httpBinding, epa); var res = tcClient.addTicket(ticket); // 设置字段值,分阶段处理 if (context.Stage == 20) // PreOperation,直接修改Target { entity["new_resultfield"] = res.ToString(); } else // PostOperation,用UpdateRequest { Entity updateEntity = new Entity("incident", entity.Id); updateEntity["new_resultfield"] = res.ToString(); service.Update(updateEntity); } } catch (Exception ex) { throw new InvalidPluginExecutionException("Failed to register ticket by this error: " + ex.Message); } }
内容的提问来源于stack exchange,提问作者Mehdi

