Wednesday, August 28, 2013

Entity.GetAttributeValue<T> Explained

The CRM 2011 SDK offers a handful of useful extensions over the previous versions that many Dynamics CRM developers have come to appreciate.  One in particular that I’ve only recently come to use is the Entity.GetAttributeValue<T> method.  I’ve seen it used in many places, and have started using it myself, but I never really fully understood how it can be expected to behave.  The Microsoft documentation—as with a great deal of the SDK—doesn’t offer much on this particular method, and no example code.

I’ve found myself continuing to lean on Entity.Contains as a measure of safety, and checking the Entity indexer for type (using the “is” keyword), to make sure I wouldn’t violate a Type constraint.  My concerns have been alleviated today when I took a few minutes to run some scenarios through the method, to see what the results were.  There’s some good news I’d like to share.

The given key was not present

The only method parameter taken by Entity.GetAttributeValue<T> is the name of an attribute.  If that attribute is not present in the Entity instance, it will not generate an error.  Instead, the method will return a “default value” of Type T, or null if T is nullable.

Be careful of assuming this means that the Entity contains your attribute, however, because it may not.  Entity.Contains will assure that a null return from Entity.GetAttributeValue<T> means that the attribute is truly null, and not just effectively null because it is missing.

Nullable Types

The non-primitive types provided by the SDK are all nullable (e.g. OptionSetValue, Money), meaning returns of these types will be a complete instance of the Type, or null.  However, the primitive types the SDK uses are not inherently nullable (e.g. int, bool).

What happens if you pass a non-nullable type into the Type parameter T, and the value for requested attribute is, in fact, null?  Well, thankfully the SDK converts the null into a bitwise 0 value for T.  This is what I call the “default value”, and I’ll get to that in a moment.

If you prefer to always receive null, instead of the “default value”, then you can pass a nullable-extended, primitive Type (e.g. int?, bool?, decimal?), and the SDK will cast the requested attribute accordingly.

Default values

A “default value” is the result of a null return being coerced into a non-nullable type.  The null return could be either: a) a missing attribute, or b.) a null value on the attribute.  When converting null, the SDK selects a bitwise 0 value for your type.  Consult the chart below for the expected returns:


Numerical (int, decimal, double) 0
Boolean false
DateTime DateTime.MinValue
Guid Guid.Empty

This can be handy, if you are performing calculations on primitive types, and would rather not write several lines of code to weed out null values (since performing any mathematical operation on null results in null).

Specified cast is not valid

If, by some misfortune, you pass a Type to the T parameter that is not Metadata-compatible with the actual value of the attribute you requested, this method will thankfully throw an InvalidCastException, rather than returning a default value.  This assures you that you’re not mismatching Types.

For example, if an attribute is int and has an int value, but I pass ‘decimal’ to T, my code will throw an InvalidCastException.  Even though casting an int to a decimal is trivial for .Net, if my supplied Type does not match the Metadata schema, I will run into this exception.


So there you have it, a full rundown of how Entity.GetAttributeValue<T> works and how you can expect it to behave in your code.  Hopefully, you’ll realize like I did, that you can save yourself a lot of code by using this method and relying on its undocumented behaviors.  It was a fantastic add by the Dynamics CRM development team, and a hidden treasure for developers like me who became accustomed to writing all manner of attribute evaluations with previous versions of the product!

No comments:

Post a Comment

Unrelated comments to posts may be summarily disposed at the author's discretion.