这是我的第503篇原创文章,写于2023年9月7日。
碰到一个奇怪的问题,用户在界面上更新记录不报错,
但是通过代码使用Web API来更新代码会报错,使用的是普通的更新代码:
var clientUrl = Xrm.Utility.getGlobalContext().getClientUrl();
var req = new XMLHttpRequest();
var requestData = {
"ly_name": "罗勇"
};
req.open("PATCH", `${clientUrl}/api/data/v9.2/ly_demoentitiess(93a0d8f8-8c5b-4a4e-aeb2-7c7e7a18fc79)`);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Mscrm.Suppressduplicatedete", "false");
req.onreadystatechange = function () {
if (this.readyState == 4) {
req.onreadystatechange = null;
if (this.status == 204) {
console.log("updated");
}
else {
var error = JSON.parse(this.response).error;
Xrm.Navigation.openErrorDialog({ message: error.message });
}
}
}
req.send(JSON.stringify(requestData));
碰到的错误返回信息如下:
{
"error": {
"code": "0x8004023b",
"message": "Found multiple records While trying to resolve alternate key."
}
}
后来经过逐步排查,是这个实体的RetrieveMultiple 的Pre Operation阶段的插件代码修改了查询条件,增加了一些关联表,关联后会使返回的记录翻倍,比如说客户和联系人关联,会导致返回的客户记录翻倍,当然,实际查询结果的话,如果添加了Distinct = 'true' 的话可能看到的结果是没有问题的。
当然我们觉得奇怪的是,更新记录,指定了记录ID,为啥会再走一次RetrieveMultiple 消息了,估计啊,通过代码更新记录的时候,这个查询是通过QueryExpression来做的,那怕查询条件指定了查询记录的主键,也会通过RetrieveMultiple来查询出这条记录,而不是通过Retrieve来查找出这条记录。而在界面表单修改后保存,查询记录走的Retrieve消息,所以没有问题。
大致了解了问题原因后,那么解决办法就是要让指定记录主键进行的查询的时候不要再返回翻倍的结果就可以,经过尝试后,的确这样可以解决问题。
方法比较简单,就是如果RetrieveMultiple消息中的查询条件的查询字段是记录的主键字段,操作符是eq的话,就不要再和其他表关联,也不要修改筛选条件,因为既然是根据主键来查找记录,最多也就一条,再加其他筛选条件意义不大。
private static string BuildFilterXmlForQuery(string fetchXml, RetieveMultipleFilterConfig config)
{
if (string.IsNullOrEmpty(fetchXml)) return string.Empty;
if (config == null) return fetchXml;
try
{
XDocument fetchXmlDoc = XDocument.Parse(fetchXml);
if (!!config.Distinct)
{
SetAttributeNode(fetchXmlDoc.Root, "distinct", "true");
}
SetAttributeNode(fetchXmlDoc.Root, "no-lock", "false");
var entityElement = fetchXmlDoc.Descendants("entity").FirstOrDefault();
if (entityElement != null)
{
var entityLogicalName = entityElement.Attribute("name").Value;
var conditionList = fetchXmlDoc.Descendants("condition").ToList();
if (conditionList.Any(condition => condition.Attribute("operator").Value.Equals("eq") && condition.Attribute("attribute").Value.Equals($"{entityLogicalName}id")))
{
return fetchXmlDoc.ToString();
}
if (!string.IsNullOrEmpty(config.Filter))
{
entityElement.Add(XElement.Parse(config.Filter));
}
if (config.LinkEntities != null && config.LinkEntities.Any())
{
foreach (var linkEntity in config.LinkEntities)
{
entityElement.Add(XElement.Parse(linkEntity));
}
}
}
return fetchXmlDoc.ToString();
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException("An error occurred in the processing query filter.", ex);
}
}