Search code examples
c#dynamics-crmquery-expressions

Dynamics CRM QueryExpression nested LinkedEntity


I'm trying to retrieve data from the Contact entity, left joining that to Function entity and then joining the Function to an Account entity.

The idea is to use a single query to retrieve contact data plus some extra fields from the other two.

I'm able to join and retrieve data from a the Function linked entity, but I'm having trouble understanding how to work with nested linked entities.

QueryExpression queryExpression = new QueryExpression(Contact.EntityLogicalName);

queryExpression.ColumnSet = new ColumnSet(Contact.Fields.pix_GZGId, Contact.Fields.Id, Contact.Fields.FirstName, Contact.Fields.LastName, 
    Contact.Fields.MiddleName, Contact.Fields.GenderCode, Contact.Fields.EMailAddress1, Contact.Fields.EMailAddress2, 
    Contact.Fields.Telephone2, Contact.Fields.MobilePhone, Contact.Fields.pix_Gastlessenoptin);
FilterExpression emailFilter = new FilterExpression(LogicalOperator.Or);

emailFilter.AddCondition(Contact.Fields.EMailAddress1, ConditionOperator.Equal, email);
emailFilter.AddCondition(Contact.Fields.EMailAddress2, ConditionOperator.Equal, email);

FilterExpression nameFilterAll = new FilterExpression(LogicalOperator.And);
nameFilterAll.AddCondition(Contact.Fields.FirstName, ConditionOperator.Equal, firstname);
nameFilterAll.AddCondition(Contact.Fields.LastName, ConditionOperator.Equal, lastname);

if (!String.IsNullOrEmpty(middlename))
{
    nameFilterAll.AddCondition(Contact.Fields.MiddleName, ConditionOperator.Equal, middlename);
}

FilterExpression nameFilterPartial = new FilterExpression(LogicalOperator.And);
nameFilterPartial.AddCondition(Contact.Fields.FirstName, ConditionOperator.BeginsWith, firstname.Substring(0,1));
nameFilterPartial.AddCondition(Contact.Fields.LastName, ConditionOperator.Equal, lastname);

FilterExpression nameFilterLast = new FilterExpression();
nameFilterLast.AddCondition(Contact.Fields.LastName, ConditionOperator.Equal, lastname);

FilterExpression completeNameFilter = new FilterExpression(LogicalOperator.Or);
completeNameFilter.Filters.Add(nameFilterAll);
completeNameFilter.Filters.Add(nameFilterPartial);
completeNameFilter.Filters.Add(nameFilterLast);

queryExpression.Criteria = new FilterExpression(LogicalOperator.And);
queryExpression.Criteria.Filters.Add(emailFilter);
queryExpression.Criteria.Filters.Add(completeNameFilter);

queryExpression.AddOrder(Contact.Fields.LastName, OrderType.Descending);

//this is the problem part - it doesn't seem to do anything.
LinkEntity linkedFunctions = new LinkEntity(Contact.EntityLogicalName, pix_functie.EntityLogicalName, 
    Contact.Fields.Id, pix_functie.Fields.pix_Persoon, JoinOperator.LeftOuter);
linkedFunctions.Columns = new ColumnSet(pix_functie.Fields.Id, pix_functie.Fields.pix_Organisatie, 
    pix_functie.Fields.pix_isgzgfunction, pix_functie.Fields.pix_omschrijving, pix_functie.Fields.CreatedOn);
linkedFunctions.EntityAlias = ConfigKeys.PixFunctieAlias;

FilterExpression accountFilterExpression = new FilterExpression();
//accountFilterExpression.Conditions.Add(new ConditionExpression(pix_functie.Fields.pix_isgzgfunction, ConditionOperator.Equal, true));

linkedFunctions.AddLink(Account.EntityLogicalName, pix_functie.Fields.pix_Organisatie, Account.Fields.Id, JoinOperator.LeftOuter);
linkedFunctions.LinkEntities[0].LinkCriteria = accountFilterExpression;
linkedFunctions.LinkEntities[0].EntityAlias = "linkedAccountAlias";

queryExpression.LinkEntities.Add(linkedFunctions);

request.Query = queryExpression;

RetrieveMultipleResponse response = (RetrieveMultipleResponse)_context.Execute(request);

This gets me data from the Function linked entity no problem, which I then group, order and process:

    IEnumerable<Microsoft.Xrm.Sdk.Entity> entities = response.EntityCollection.Entities   
            .OrderByDescending(x => (x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_isgzgfunction) != null ? (bool?)
                x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_isgzgfunction).Value : new Nullable<bool>()).HasValue)
            .ThenByDescending(x => (x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_isgzgfunction) != null ? (bool?)
                x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_isgzgfunction).Value : null))
            .ThenByDescending(x => (x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.CreatedOn) != null ? (DateTime?)
                x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.CreatedOn).Value : new Nullable<DateTime>()).HasValue)
            .ThenByDescending(x => (x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.CreatedOn) != null ? (DateTime?)
                x.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.CreatedOn).Value : null));


    List<TtNugGzgDataContract.Entities.Contact> lvl1Contacts = new List<TtNugGzgDataContract.Entities.Contact>();
    List<TtNugGzgDataContract.Entities.Contact> lvl2Contacts = new List<TtNugGzgDataContract.Entities.Contact>();

    foreach(var entity in entities)
    {
        Contact contact = entity.ToEntity<Contact>();
        TtNugGzgDataContract.Entities.Contact contactToAdd = new TtNugGzgDataContract.Entities.Contact()
        {
            externalID = contact.Id,
            firstname = contact.FirstName,
            lastname = contact.LastName,
            insertion = contact.MiddleName,
            gender = Converter.GetApiGender(contact.GenderCodeEnum),
            phone = !String.IsNullOrEmpty(contact.Telephone2) ? contact.Telephone2 : contact.MobilePhone,
            newsletter = contact.pix_GastlessenoptinEnum == pix_nieuwsbriefoptin.Toestaan
        };


        contactToAdd.entityID = entity.GetAttributeValue<AliasedValue>("linkedAccountAlias" + "." + Account.Fields.pix_GZGId) != null ?
            (string)entity.GetAttributeValue<AliasedValue>("linkedAccountAlias" + "." + Account.Fields.pix_GZGId).Value : null;

        contactToAdd.function = entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_omschrijving) != null ?
            (string)entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_omschrijving).Value : null;

        contactToAdd.isGzg = entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_isgzgfunction) != null ?
            (bool?)entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_isgzgfunction).Value : null;

        contactToAdd.createdOn = entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.CreatedOn) != null ?
            (DateTime?)entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.CreatedOn).Value : null;

        EntityReference reference = entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_Organisatie) != null ?
            (EntityReference)entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_Organisatie).Value : null;



        contactToAdd.accId = (entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_Organisatie) != null ?
            (EntityReference)entity.GetAttributeValue<AliasedValue>(ConfigKeys.PixFunctieAlias + "." + pix_functie.Fields.pix_Organisatie).Value : new EntityReference()).Id;


        if (!String.IsNullOrEmpty(contact.EMailAddress1) && contact.EMailAddress1.Equals(email, StringComparison.InvariantCultureIgnoreCase))
        {
            contactToAdd.email = contact.EMailAddress1;
            contactToAdd.isSecondaryEmail = false;
            lvl1Contacts.Add(contactToAdd);
        } else
        {
            contactToAdd.email = contact.EMailAddress2;
            contactToAdd.isSecondaryEmail = true;
            lvl2Contacts.Add(contactToAdd);
        }
    }

But I can't get the entityID out of the nested linked entity. AFAIK the alias is correct, I've even tried prepending it with the the alias of the first level linked entity to no effect.

Can anybody point me in the right direction?


Solution

  • Rather than hand-rolling your QueryExpression, I suggest using a tool to do it for you.

    1. Download and install XrmToolBox (http://www.xrmtoolbox.com).
    2. Download the FetchXml Builder from the Plugin Store (on the tools menu).
    3. Build your query using FetchXml in the graphical interface.
    4. On the View menu, choose "QueryExpression."
    5. A window will pop up on the right showing you the generated QueryExpression for your FetchXml query.

    Hope that helps!