Konrad Viltersten
Konrad Viltersten

Reputation: 39298

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

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

Upvotes: 7

Views: 14770

Answers (4)

Vinod Srivastav
Vinod Srivastav

Reputation: 4263

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.

Upvotes: 0

Matt
Matt

Reputation: 1370

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")

Upvotes: 0

Manuel Roldan
Manuel Roldan

Reputation: 535

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;

Upvotes: 2

Peter Majeed
Peter Majeed

Reputation: 5362

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
};

Upvotes: 8

Related Questions