How to access values from fetchedXML when using aliases to attributes in CRM Dynamics?

Konrad Viltersten picture Konrad Viltersten · Sep 28, 2012 · Viewed 8.6k times · Source

I'm fetching a list of marketing lists. As I do so, it seems to be a successful operation according to my check using intellisense. When I look for ...Entities[0].Attributes["nick"] I get an object (with the right data somewhere in it). But I can't access it programmatically (instead I have to click around like a monkey via the pluses to fold out the good stuff).

In fact, I'm getting the entities as supposed to using the code below. The problem is that they aren't Strings according to the computer. They are of type Microsoft.Xrm.Sdk.AliasedValue and I don't know how to access the actual nick inside them.

new Contact
{
  Name = element.Attributes["nick"] as String,
  Mail = element.Attributes["mail"] as String
}

Intellisense says that Value is in there (and it's the correct value too) but I can't access it by typing .Value. I suspect that I need to use "as" or something like that but at the moment I'm stuck. Any hints? As'ing it to String, which is supposed to work, gives me null...

I've read this article and several others like it and the way I see it, I'm supposed to be able to access all the fun stuff in there. I can't though...

I've noticed that the following code gets me the data I'm so desperately trying to get but this can't be a professional syntax, can it?! Seriously, it looks like a high school student with ADHD and hangover tried to do that...

new Contact
{
  Name = ((Microsoft.Xrm.Sdk.AliasedValue)result.Entities[0].Attributes["nick"]).Value,
  Mail = ((Microsoft.Xrm.Sdk.AliasedValue)result.Entities[0].Attributes["mail"]).Value
}

I mean, seriously - this is one ugly piece of code... There's got to be a better way! However, I fear there's not because this discussion seems to be using that syntax as well...

Answer

Peter Majeed picture Peter Majeed · Sep 29, 2012

Looking at the documention, the Attributes property of the Entity object is of type AttributeCollection, which derives from DataCollection<string,Object>.

For each attribute in the collection there is a key/value pair.

So for each key ("nick", "mail"), there is a corresponding object, which could be of any .NET type. You have to cast the object to the correct type (as you've done) to access the properties you're looking for (or else use reflection, which would certainly be uglier, or I suppose in C# 4.0 the dynamic type, but in that case, you lose compile-time checking); how else would the compiler be able to determine whether an attribute is of type string/Money/int/AliasedValue/etc.?

As for AliasedValue, the CRM uses this type to store additional information about the returned value, and since any attribute can be aliased, the Value property could be of any type (OptionSetValue, decimal, string, Guid, EntityReference, etc.). The Value property is then appropriately also of type object, so you have to cast this as well to get any additional information about your returned value.

So there's no way around casting, but you can make your code shorter and perhaps cleaner by adding a using statement at the top of your file and by defining the values of each AliasedValue prior to the assignment to the Contact. Regardless, I've included one example of each type of data retrieval; you can judge which is better in your project.

Using casting:

using Microsoft.Xrm.Sdk;

...

var nick = (AliasedValue)result.Entities[0].Attributes["nick"];
var mail = (AliasedValue)result.Entities[0].Attributes["mail"];

var contact = new Contact
{
    Name = nick.Value, //Value is of type object; cast again for a more specific type
    Mail = mail.Value
};

Using reflection:

var nick = result.Entities[0].Attributes["nick"]
    .GetType()
    .GetProperty("Value")
    .GetValue(result.Entities[0].Attributes["nick"], null);
var mail = result.Entities[0].Attributes["mail"]
    .GetType()
    .GetProperty("Value")
    .GetValue(result.Entities[0].Attributes["mail"], null);

var contact = new Contact
{
    Name = nick,
    Mail = mail
};

Using dynamic:

dynamic nick = result.Entities[0].Attributes["nick"];
dynamic mail = result.Entities[0].Attributes["mail"];

var contact = new Contact
{
    Name = nick.Value, //dynamic figures out the right property at runtime
    Mail = mail.Value
};