I was recently working at a client site and came across the error/exception below. A plugin I had developed a few weeks earlier stopped working in the integration environment after an update to a different class/plugin. The error did not make sense and sent me on a wild goose chase for a few hours. How can code function in one environment and not in another environment, when one environment is a replicate of the other and all the parameters as well as infrastructure is the same? As a software developer, the favorite part of my job is coming across such conundrums.
The Error
This was as much information as I could get from the system. This exception indicates that I was trying to access an attribute that was not present in the internal .NET dictionary. I looked very closely at both environments and they had all the entity attributes I was accessing in the plugin.
In investigating this error, I came across a more efficient way to access entity attributes in Microsoft Dynamics 365. Often, before Dynamics 365 software developers perform logic on a retrieved value, they have to check if the retrieved value exists. Executing such logic usually follows the following path or something similar (we use the Contact entity in this example but this can be applied to any out of the box entity or custom entity):
Accessing attributes using Attributes.Contains() method
//Declare the contact entity and retrieve it from the execution context Entity contact = pluginExecutionContext.InputParameters["Target"] as Entity; //Check if attribute exists in collection, using
Attributes.Contains if (contact.Attributes.Contains("company")) { //Get attribute of interest, as a string string contactCompany = contact.GetAttributeValue("company"); //Perform operations on your attribute of interest /* */ }
Accessing attributes using Attributes.TryGetValue() method
//Declare the contact entity and retrieve it from the execution context Entity contact = pluginExecutionContext.InputParameters["Target"] as Entity; //Object that will store the attribute object if the retrieval is successful Object contactComp; //Check if attribute exists in collection, using Attributes.TryGetValue if (contact.Attributes.TryGetValue("company", out contactComp)) {
//Get attribute of interest, as a string string contactCompany = (String)contactComp;
//Perform operations on your attribute of interest /* */ }
Despite the Attributes.Contains() method and the Attributes.TryGetValue() being used to achieve the same objective, the latter is more efficient. When you use the Attributes.Contains() method, your code is performing two look up operations in the background. First, it checks if the specified attribute is contained in the internal dictionary. If the dictionary contains the specified attribute, another look up will be performed to get the corresponding value for the attribute. The Attributes.TryGetValue() method performs both operations in one swoop by returning a Boolean value, true (value exists) or false (value does not exist), and an output object, which contains the attribute value.
If you are only accessing the attribute dictionary as shown above, a few times, you may not see significant savings in execution time, by using Attributes.TryGetValue(). However, as the application grows and you are performing these operations regularly or in a loop, the code optimizations benefits of using Attributes.TryGetValue() become self-evident.
To learn more about Attributes.TryGetValue() method, look at the Microsoft documentation: TryGetValue().
Conclusion
The Attributes.TryGetValue() method resolved the exception I was getting within my plugin and optimized my code. This is more than what I had bargained for when I began my investigation, which is a terrific and extremely rewarding outcome. Therefore, I would recommend the Attributes.TryGetValue() method over the Attributes.Contains() method.