I was writing some code recently for a CRM plugin, that involved working with multiple link entity attributes as part of a QueryExpression. Accomplishing this is relatively straightforward, thanks to some of the code samples provided within the SDK. For example, the following sample code gives you everything you would need; how to build the query and, more importantly, include the attributes you would need to return as part of the request:
Very handy! But there are two problems that you may encounter when attempting to run the code yourself:
- The code snippet attempts to print out the value of the LinkEntity First Name and Last Name Contact fields to the console. So, for example, when your code retrieves the Fourth Coffee (sample) record, we would expect to see Rene Valdes (sample) printed out. Unfortunately, this doesn’t happen, and we are instead greeted with a value that may precipitate some serious head-scratching:The issue lies in how the SDK handles link-entity attributes included as part of a query. Instead of returning the value in the data type we would expect and, despite appending the appropriate namespace within our code to indicate the link entity our attribute is from (in this case, primarycontact), CRM instead gives us the attribute as part of an AliasedValue object. The comforting thing to know is that the attribute value that we may (desperately!) want to grab is in this object somewhere, and we will look at the best way of accessing this later on in this post.
- Putting aside the above issue, let’s assume that we weren’t working with an AliasedValue and just a standard CRM data type - for example, Single Line of Text/string. Our above code would work fine against all of the sample data records within CRM. But if we add a new Account record and add a Primary Contact record that doesn’t have a First Name value, we get an error thrown within our code when it attempts to output this value into the console:Regular readers of the blog may be able to guess what is happening - as part of one of my previous posts looking at the GetAttributeValue method, we looked at the danger of accessing attributes via the Entity class, particularly when these attributes may end up containing blank values. In this particular example, there is another factor at play too - when a QueryExpression attempts to bring back all of the requested attributes, any attribute(s) that do not contain a value in the CRM database (i.e. are NULL) are not returned at all. This fact is useful in ensuring that our code can run as optimally as possible, but in this case, is our downfall - because our Entity object has no Contact attribute with a First Name value, no value is available to return, so our code panics and throws an error; for the simple reason that it is attempting to access something that does not exist.
So how can we get around both of these issues? First of all, we need to alter the code so that the actual value of the First Name and Last Name fields are displayed correctly. One way of executing this is to explicitly cast our attribute values as AliasedValues, access the inner properties of the object and then cast to string the value of the attribute. An updated version of our code would, therefore, look like this:
Running this against our Sample Account/Contact results returns what we expect, which is good :)
But, unfortunately, does not give us a complete solution to the above as we still hit a “The given key was not present in the dictionary.” error message as before, for the same reasons:
The above method is not satisfactory then, as we would need to include additional lines of code to handle the fact that the attribute value is not present - an if…else statement or a try…catch block. If we forget to put this in, then another issue surfaces in bad code potentially seeping into a Production environment - causing errors for end-users and hours of unnecessary debugging.
Our old friend GetAttributeValue has saved the day for us previously; can it do again? We can try by using the code snippet below - specifying the fact that we are returning an AliasedValue object and then getting the inner attribute Value:
The code errors again, unfortunately, with a different error message, indicating that a NULL value is attempting to be accessed:
In this case, we must rely on our knowledge of C# to save the day, with a few options at our disposal. I’ve already suggested two possible options, but another could be considered, which would reduce the number of lines in our code. By using the conditional operator (?:), we can “test” for a NULL value in our GetAttributeValue and, if a NULL value is present, substitute it for an empty string or a value of our choice. Our final, error-free code, would look like this:
Then, just to be sure, we run a quick test and confirm everything works as expected:
Now we can rejoice that our code is error free and that we’ve found another good example of how the GetAttributeValue method should always be used when working with CRM attributes. It is a shame though that the code above, provided by Microsoft as part of the SDK, has such a significant error within it. Hopefully, it will be addressed as part of a future version of the SDK and we should be thankful that we at least have all of the sample code in the SDK in the first place; it gives developers and CRM customisers a great starting point to start developing consistent and supported code.