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?
Rather than hand-rolling your QueryExpression, I suggest using a tool to do it for you.
Hope that helps!