Disclaimer – this blog post only applies to the Community Edition of CRM Portals, as you require access to the source code.

The problem – Support for EntityCollections in Liquid

When working with Liquid and CRM Portals you might have noticed that Activityparties are not supported. Say for example you have a FetchXml on emails and would like to display the “to”-recipients.

Your FetchXML would look like this:

{% fetchxml emails %}
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="email">
    <attribute name="sender" />
    <attribute name="to" />
    <attribute name="subject" />
    <filter type="and">
      <condition attribute="createdon" operator="last-x-weeks" value="6" />
    </filter>
  </entity>
</fetch>
{% endfetchxml  %}

And when displaying the results, you’ll receive the following output:

{
  "from" : "Nathan Vanblaere",
  "to" : "Liquid syntax error: Object 'Microsoft.Xrm.Sdk.EntityCollection' is invalid because it is neither a built-in type nor implements Iliquidizable",
  "subject" : "Test Email 1"
},

{
  "from" : "Nathan Vanblaere",
  "to" : "Liquid syntax error: Object 'Microsoft.Xrm.Sdk.EntityCollection' is invalid because it is neither a built-in type nor implements Iliquidizable",
  "subject" : "Test Email 2"
}

As you can see, the to-field is not rendered properly. Instead it lists the following error message:

“Liquid syntax error: Object ‘Microsoft.Xrm.Sdk.EntityCollection’ is invalid because it is neither a built-in type nor implements Iliquidizable”

This is because the to-field is an Entity Collection (of type Activityparty), which does not seem to be supported by the CRM Portals. It’s almost as if ADX / Microsoft forgot to implement the EntityCollection as an ILiquidizable object. For more information on how to make an object available for liquid, please refer to the DotLiquid-Drops Github page.

However, when we look at the code we can see that there is actually a file called EntityCollectionDrop.cs under Adxstudio.XRM > Web > Mvc > Liquid, which would seem to indicate that the code above should work.

CRM Portals - Liquid EntityCollection

So why doesn’t it?

Well, the error might be a bit misleading. The problem is actually not that the EntityCollection is not implemented, because it is. The key issue is that the EntityCollection is not implemented as a property of the EntityDrop class. When looking at the EntityDrop class, you’ll notice a method called TransformAttributeValueForLiquid.

CRM Portals - TransformAttributeValueForLiquid
In this method, you can see that attributes are formatted according to their corresponding types… but there’s no implementation for the EntityCollection object. Aha, found it!

The solution

The solution is quite simple, we just have to modify/extend this method to include a way to format EntityCollection objects. Formatting can be done as per your choice, the code I used below will simply concatenate the ‘name’ values of entities with a semicolon. I’ve added an additional check for activity parties, as for activity parties, you’ll want the name of the Party-field.

var collection = value as EntityCollection;
if(collection != null && collection.Entities.Count() > 0)
{
	//check for entityMetadata
	RetrieveEntityResponse entityMetadata = (RetrieveEntityResponse) this.PortalOrganizationService.Execute(new RetrieveEntityRequest
	{
		EntityFilters = EntityFilters.Entity,
		LogicalName = collection.EntityName
	});

	if(collection.EntityName == "activityparty")
	{
		List<string> names = new List<string>();
		foreach (Entity item in collection.Entities)
		{
			EntityReference party = item.GetAttributeValue<EntityReference>("partyid");
			if(!string.IsNullOrEmpty(party?.Name))
			{
				names.Add(party.Name);
			}
			else
			{
				string addressUsed = item.GetAttributeValue<string>("addressused");
				names.Add(addressUsed);
			}
		}

		return String.Join(";", names);
	}
	else
	{
		string nameField = entityMetadata.EntityMetadata.PrimaryNameAttribute;
		IEnumerable<string> names = collection.Entities.Select(x => x.GetAttributeValue<string>(nameField));
		return String.Join(";", names);

	}
}

Add the code above to the method, just before the return statement.
Your output will now look something like this:

{
  "from" : "Nathan V",
  "to" : "Kris S;Wouter B",
  "subject" : "Test Email 1"
},

{
  "from" : "Nathan V",
  "to" : "Kris S;Wouter B",
  "subject" : "Test Email 2"
}