6

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...

Peter Majeed
  • 5,304
  • 2
  • 32
  • 57
Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
  • There's nothing wrong with the cast – James Wood Sep 29 '12 at 21:47
  • @JamesWood Haha, say that to my former professor at the university. "If you need to cast, you've got a bad design. Do-over!" I guess I was right that **in some cases** casting **is** correct but he'd probably blame Microsoft in this case. It was back in the 90's when Java was the golden child and the only way to go. MS was evil and a spawn of Satan, haha. – Konrad Viltersten Sep 30 '12 at 13:10
  • Well if you dont want to cast you can use the strongly typed entities. The point of the late bound Entity is to allow you to be flexible, which in this case means you have to cast once in a while. Also in this case youre not casting to change the object (e.g int to decimal), youre casting so you have access to all the properties. – James Wood Sep 30 '12 at 18:43
  • @JamesWood Very good point. Thanks! – Konrad Viltersten Oct 01 '12 at 07:17

4 Answers4

7

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
};
Community
  • 1
  • 1
Peter Majeed
  • 5,304
  • 2
  • 32
  • 57
  • Oh, that was nice. I'm not delighted but it's still nice. In a way I'm feeling satisfied that **my** approach (ugly as it may be) was the one recommended by others. I'll keep it since it's the fastest way. +1 for all the above. You deserve to get more but SO rules the way they are. As for the `using`, I have that in my code. Here I used the full path to make a point. – Konrad Viltersten Sep 28 '12 at 23:17
1

I think you wanted something like this:

 //Get Account PrimaryContact details
        public static void GetAccountPrimaryContactDetails(Guid accountId, IOrganizationService orgService)
        {
            var contactFirstName = default(object);
            var contactLastName = default(object);
            var contactFullName = default(object);

            string fetchXML = string.Format(@"<fetch version='1.0' output-format='xml-platform' no-lock='true' mapping='logical'>
                                    <entity name='account'>
                                        <attribute name='name' />                                                                                
                                        <filter type='and'>
                                            <condition attribute='statuscode' operator='eq' value='1' />                                            
                                            <condition attribute='accountid' operator='eq' value='{0}' />
                                        </filter>                                        
                                        <link-entity name='contact' from='contactid' to='primarycontactid' alias='ab'>
                                             <attribute name='fullname' alias='as_fullname' />
                                             <attribute name='firstname' alias='as_firstname' />                                             
                                             <attribute name='lastname' alias='as_lastname' />
                                        </link-entity>
                                    </entity>
                                </fetch>", accountId.ToString());

            var fetchExp = new FetchExpression(fetchXML);

            EntityCollection accountEntity = orgService.RetrieveMultiple(fetchExp);

            if (accountEntity.Entities.Count > 0)
            {
                //Primary Contact Fullname
                AliasedValue avContactFullname = accountEntity.Entities[0].GetAttributeValue<AliasedValue>("as_fullname");
                if (avContactFullname != null)
                    contactFullName = avContactFullname.Value;
                //Primary Contact Firstname
                AliasedValue avContactFirstname = accountEntity.Entities[0].GetAttributeValue<AliasedValue>("as_firstname");
                if (avContactFirstname != null)
                    contactFirstName = avContactFirstname.Value;
                //Primary Contact Lastname
                AliasedValue avContactLastname = accountEntity.Entities[0].GetAttributeValue<AliasedValue>("as_lastname");
                if (avContactLastname != null)
                    contactLastName = avContactLastname.Value;
Manuel Roldan
  • 487
  • 3
  • 12
0

We have a GetValue helper function defined which handles checking for the aliased values for you

public static T GetValue<T>(this Entity entity, string attributeName)
{
     if (entity.Attributes.ContainsKey(attributeName))
    {
        var attr = entity[attributeName];
        if (attr is AliasedValue)
        {
            return GetAliasedValueValue<T>(entity, attributeName);
        }
        else
        {
            return entity.GetAttributeValue<T>(attributeName);
        }
    }
    else
    {
        return default(T);
    }

}

This uses an internal extra helper method to get the aliased field value

private static T GetAliasedValueValue<T>(this Entity entity, string attributeName)
        {
            var attr = entity.GetAttributeValue<AliasedValue>(attributeName);
            if (attr != null)
            {
                return (T)attr.Value;
            }
            else
            {
                return default(T);
            }

        }

It's defined as an extension method on the Entity class so you can use it in the same way as the SDK provided GetAttributeValue - you just have to remember to use the aliased field name.

var myValue = myEntity.GetValue<string>("alias.fieldname")
Matt
  • 1,370
  • 2
  • 16
  • 40
0

One more alternative is to use this construct:

public static T GetAliasedAttributeValue<T>(Entity entity, string attributeName)
{
    if (entity == null)
        return default(T);

    AliasedValue fieldAliasValue = entity.GetAttributeValue<AliasedValue>(attributeName);

    if (fieldAliasValue == null)
        return default(T);

    if (fieldAliasValue.Value != null && fieldAliasValue.Value.GetType() == typeof(T))
    {
        return (T)fieldAliasValue.Value;
    }

    return default(T);
}

this can be calledeasily as:

Entity entity; //the result
var businessUnitGuid = GetAliasedAttributeValue<Guid>(entity, "bunit.businessunitid");

where entity is the resulted entity and bunit is the alias name.

Vinod Srivastav
  • 3,644
  • 1
  • 27
  • 40