从 Dynamics CRM 插件调用异步库

Calling asynchronous library from Dynamics CRM Plugin

我有一个连接到第三方系统的异步库 API。我正在尝试在 Dynamics C# 插件中使用此库在第三方系统中创建新记录。只要插件一次只在一个实体上运行,我编写的代码就可以正常工作。但是,如果我同时在两个不同的实体上启动插件,我会收到错误消息:

Failed to update Star. Error - Object reference not set to an instance of an object. : System.NullReferenceException : : at COHEN.APIConnector.Connectors.StarConnector.d__2`1.MoveNext()

我不太确定是什么原因导致此错误或如何解决它。这似乎与我为连接 API 而编写的库的异步性质有关。什么会导致此错误,有哪些解决方法?

插件代码

APIResponse<COHEN.APIConnector.Model.Entity.ContractJob> response = new APIResponse<COHEN.APIConnector.Model.Entity.ContractJob>();

Task.WaitAll(Task.Run(async () => response = await starConnector.Create(starJob))); 

图书馆代码

public async Task<APIResponse<T>> Create<T>(T entity) where T : EntityBase
{
    APIResponse<T> response = new APIResponse<T>();

    try
    {
        using (HttpClient client = new HttpClient())
        {
            client.BaseAddress = new Uri(Helpers.GetSystemUrl(Application.Star));
            client.DefaultRequestHeaders.Clear();
            client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));

            response.RequestURL = "Calling ToJSON";

            string json = await entity.ToJSON(Application.Star);

            response.RequestURL = "JSON: " + json;

            response.RequestURL = "RunTask?taskid=" + (int)TaskID.CREATE + "&entity=" +
                await MapSingleton.Instance.GetFieldName(Application.Star, entity.Type, FieldType.EntityName) +
                "&json=" + json;

            using (HttpResponseMessage responseMessage = await client.GetAsync(
                "RunTask?taskid=" + (int)TaskID.CREATE + "&entity=" +
                await MapSingleton.Instance.GetFieldName(Application.Star, entity.Type, FieldType.EntityName) +
                "&json=" + json
            ))
            {
                // Check TaskCentre response
                if (responseMessage.StatusCode == HttpStatusCode.OK)
                {
                    XmlDocument xmlDocument = new XmlDocument();
                    xmlDocument.LoadXml(await responseMessage.Content.ReadAsStringAsync());

                    // Check API Response
                    string responseStatusCode = xmlDocument.GetElementsByTagName("StatusCode").Item(0).InnerText;
                    if (responseStatusCode != "")
                    {
                        StatusCode statusCode = (StatusCode)Convert.ToInt32(responseStatusCode);
                        string statusMessage = xmlDocument.GetElementsByTagName("StatusMessage").Item(0).InnerText;

                        if (statusCode == StatusCode.Created)
                        {
                            XmlDocument xmlData = new XmlDocument();
                            xmlData.LoadXml("<data>" + xmlDocument.InnerText.Substring(0, xmlDocument.InnerText.Length - (xmlDocument.InnerText.Length - xmlDocument.InnerText.LastIndexOf("row") - 4)) + "</data>");
                            JObject data = JObject.Parse(JsonConvert.SerializeXmlNode(xmlData));

                            await response.SetValues(Application.Star, entity.Type, data["data"]["row"], entity.ID);
                        }

                        response.StatusCode = statusCode;
                        response.StatusReason = statusMessage;
                    }
                    else
                    {
                        response.StatusCode = StatusCode.Error;
                        response.StatusReason = "No Status Code Returned - " + response.StatusReason;
                    }
                }
                else
                {
                    response.StatusCode = (StatusCode)responseMessage.StatusCode;
                    response.StatusReason = responseMessage.ReasonPhrase;
                }
            }
        }
    }
    catch (Exception e)
    {
        response.StatusCode = StatusCode.Error;
        response.StatusReason = e.Message + " : " + e.GetType().ToString() + " : " + e.InnerException + " : " + e.StackTrace;
    }

    return response;
}

根据 Dynamics Developer Guide,您不应在插件中使用全局变量。我正在使用全局变量访问导致此问题的第三方库。下面链接文档的相关部分

For improved performance, Dynamics 365 for Customer Engagement caches plug-in instances. The plug-in's Execute(IServiceProvider) method should be written to be stateless because the constructor is not called for every invocation of the plug-in. Also, multiple system threads could execute the plug-in at the same time. All per invocation state information is stored in the context, so you should not use global variables or attempt to store any data in member variables for use during the next plug-in invocation unless that data was obtained from the configuration parameter provided to the constructor. Changes to a plug-ins registration will cause the plug-in to be re-initialized.