As the Dynamics CRM product evolves to provide more flexibility and extensibility for the user, changes to the underlying object model are inevitable. Microsoft must provide more robust and flexible entity attributes that allow users to customize CRM 2011 to meet their specific needs. Fortunately for developers, the programming model has been changed to use native .NET types whenever possible.  The net result is that many of the specific attributes from CRM 4.0 have been replaced with more “generic” types in CRM 2011. For a summary of these changes, take a peek here: http://technet.microsoft.com/en-us/library/gg328507.aspx

Often, developers need to capture an entity's attribute values to perform some custom function or action using a plugin. This might include updating other entities or even creating new entities based on the values derived from the base entity.

This article deals with the new OptionSetValue type, which replaces the PickList type in 4.0. It also replaces the “State” and “Status” entity attributes, which are specific PickList types. We will also read the other attributes, including the new EntityReference type which replaces a number of CRM 4.0 types such as Lookup, Customer, and Owner.  You will note that the CRM 2011 UI still refers to the “LookUp” data type, but the Attribute object model uses the EntityReference.

For examples of how we construct a plug-in and get our entity and attribute data, download the Microsoft Dynamics CRM 2011 SDK here: http://msdn.microsoft.com/en-us/dynamics/crm/default

The code below assumes we have created our service proxy from our context and implemented the Retrieve method to retrieve our entity and metadata column information. An example can be found here: http://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.iorganizationservice.retrieve.aspx

In the example reference above, and for our purposes, we will use the “Account” entity type as our basis. As in the example, we will call our account entity “retrievedAccount”.  We can iterate through the attributes of our entity as shown below. This snippet will call our custom function to read and return the value of each attribute. Note that we must call RetrieveAttributeRequest to get the metadata for our attribute itself.

   1: // Iterate the metadata columns collection
   2: foreach (KeyValuePair<string, object> metaAttribute in retrievedAccount.Attributes)
   3:      {
   4:         // Get our property object
   5:         object myProp = retrievedAccount.Attributes[metaAttribute.Key];
   6:         //
   7:         // Get the attribute metadata for the state attribute using RetrieveAttributeRequest.
   8:         RetrieveAttributeRequest attribReq = new RetrieveAttributeRequest();
   9:         attribReq.EntityLogicalName = retrievedAccount.LogicalName;
  10:         attribReq.LogicalName = metaAttribute.Key;
  11:         attribReq.RetrieveAsIfPublished = true;
  12:         RetrieveAttributeResponse amRes = (RetrieveAttributeResponse)_serviceProxy.Execute(attribReq);
  13:         AttributeMetadata attmetadata = amRes.AttributeMetadata;
  14:         // Now we call our function to read the attribute value for our entity
  15:         string sMyValue = GetMetadataValue(myProp , amRes);
  16:         Console.Write("Value of {0} is {1} ", metaAttribute.Key, sMyValue);
  17:  
  18:     }

Finally, here is our function to read and return the actual attribute value. Note that it uses attribute metadata from the RetrieveAttributeRequest to determine just how to read the value. For our OptionSetValue type, we simply put the possible values from the RetrieveAttributeRequest into an array and then find a match to the numeric value stored in our Entity’s metadata. We want to return the matching string value. For our EntityReference type, we simply return the Name property. For many attribute types we only have to return the objects string value.

   1: private string GetMetadataValue(object oAttribute, RetrieveAttributeResponse attMetadata)
   2: {
   3:     string sReturn = string.Empty;
   4:     if (oAttribute.GetType().Equals(typeof(Microsoft.Xrm.Sdk.OptionSetValue)))
   5:     {
   6:  
   7:         OptionMetadata optionList = null;
   8:         // Access the retrieved attribute.
   9:         //'Microsoft.Xrm.Sdk.Metadata.PicklistAttributeMetadata' 
  10:         if (attMetadata.AttributeMetadata.GetType().FullName.Contains("PicklistAttributeMetadata"))
  11:         {
  12:             PicklistAttributeMetadata retrievedPicklistAttributeMetadata =
  13:                 (PicklistAttributeMetadata)attMetadata.AttributeMetadata;
  14:             // Get the current options list for the retrieved attribute.
  15:             optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
  16:         }
  17:         else if (attMetadata.AttributeMetadata.GetType().FullName.Contains("StatusAttributeMetadata"))
  18:         {
  19:             StatusAttributeMetadata retrievedPicklistAttributeMetadata =
  20:                 (StatusAttributeMetadata)attMetadata.AttributeMetadata;
  21:             // Get the current options list for the retrieved attribute.
  22:             optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
  23:         }
  24:         else if (attMetadata.AttributeMetadata.GetType().FullName.Contains("StateAttributeMetadata"))
  25:         {
  26:             StateAttributeMetadata retrievedPicklistAttributeMetadata =
  27:                 (StateAttributeMetadata)attMetadata.AttributeMetadata;
  28:             // Get the current options list for the retrieved attribute.
  29:             optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
  30:         }
  31:         else
  32:             return string.Empty;
  33:         // get the text values
  34:         int i = int.Parse((oAttribute as Microsoft.Xrm.Sdk.OptionSetValue).Value.ToString());
  35:         for (int c = 0; c < optionList.Length; c++)
  36:         {
  37:             OptionMetadata opmetadata = (OptionMetadata)optionList.GetValue(c);
  38:             if (opmetadata.Value == i)
  39:             {
  40:                 sReturn = opmetadata.Label.UserLocalizedLabel.Label;
  41:                 break;
  42:             }
  43:         }
  44:  
  45:     }
  46:     else if (oAttribute.GetType().Equals(typeof(Microsoft.Xrm.Sdk.Money)))
  47:     {
  48:         sReturn = (oAttribute as Microsoft.Xrm.Sdk.Money).Value.ToString();
  49:     }
  50:     else if (oAttribute.GetType().Equals(typeof(Microsoft.Xrm.Sdk.EntityReference)))
  51:     {
  52:         sReturn = (oAttribute as Microsoft.Xrm.Sdk.EntityReference).Name;
  53:     }
  54:     else
  55:     {
  56:         sReturn = oAttribute.ToString();
  57:     }
  58:     if (sReturn == null || sReturn.Length == 0)
  59:         sReturn = "No Value";
  60:     return sReturn;
  61: }

 In the above function I have tried to cover most of the common attribute types including the Money type. You will see that there is opportunity for an empty string to be returned if we run into an unexpected type. I also have it returning the string “No Value” if the attribute exists, but is not valued. This seems to be working pretty well thus far, but if I find additional functionality to add, I will post an update. Meanwhile, feel free to experiment with this a offer comments and enhancements.