Monday, February 5, 2018

Display custom contact facets in Sitecore 9 Experience Profile

Custom contact facets

Creating custom facets is something that pops up quite often when customers start using Sitecore xDB and contact data. Sitecore 9 changed lots of things for developers working with xDB. The new xConnect layer is a wonderfull thing but when upgrading to Sitecore 9, the odds that you will need to rewrite some/all your code regarding xDB are not looking good.

Although.. you might/should see this as an opportunity to re-think what has been implemented before.

I want to focus on one particular part in this post. We had some custom facets implemented. Doing this with xConnect in Sitecore 9 is well documented on the official Sitecore documentation site.

Experience Profile

But when you have custom facets you usually also want to display this information in the Experience Profile. We used to do this based on some blog post from Adam Conn and/or Jonathan Robbins.
When upgrading to Sitecore 9, we had to made some small changes to this code. Especially (and obviously) the part where we actually do a query to fetch the data from xDB.

Contact View pipeline

As you can read in the previously mentioned blog posts the contact view pipeline is where alle the magic happens in 3 steps:
  1. Creating a data structure to hold the results of the query
  2. Executing the query
  3. Populating the data structure with the results of the query
The part where our changes are located can be found in the execution phase. Our processor will be in a pipeline referred to by a build-in Sitecore processor:
<group groupName="ExperienceProfileContactDataSourceQueries">
  <pipelines>
    <my-custom-query>
      <processor type="MyNamespace.MyDataProcessor, MyNamespace" />
    </my-custom-query>
  </pipelines>
</group>

<group groupName="ExperienceProfileContactViews">
<pipelines>
  <demo>
    ...
    <processor type="Sitecore.Cintel.Reporting.Processors.ExecuteReportingServerDatasourceQuery, Sitecore.Cintel">
      <param desc="queryName">my-custom-query</param>
    </processor>
    ...
  </demo>
</pipelines>
</group>
Let's focus on getting the data.


Query Pipeline Processors

The query pipeline processor is the part that executes the query - gets the data. It is (still) a processor based on ReportProcessorBase.

We will be using the model that was created with the custom facet. For the example we have:
  • custom facet: "CustomFacet"
  • property "Company" in this facet (type string)
The code for MyNamespace.MyDataProcessor:

public override void Process(ReportProcessorArgs args)
{
  var table = CreateTableWithSchema();
  GetTableFromContact(table, args.ReportParameters.ContactId);
  args.QueryResult = table;
}

private static DataTable CreateTableWithSchema()
{
  var dataTable = new DataTable() { Locale = CultureInfo.InvariantCulture };
  dataTable.Columns.AddRange(new[]
  {
    new DataColumn(XConnectFields.Contact.Id, typeof(Guid)),
    new DataColumn("Custom_Company", typeof(string))
  });

  return dataTable;
}

private static void GetTableFromContact(DataTable rawTable, Guid contactId)
{
  string[] facets = { CustomFacet.DefaultFacetKey };
  var contact = GetContact(contactId, facets);
  var row = rawTable.NewRow();
  row[XConnectFields.Contact.Id] = contactId;

  if (contact.Facets.TryGetValue(CustomFacet.DefaultFacetKey, out var customFacet))
  {
    row["Custom_Company"] = ((CustomFacet)customFacet)?.Company;
  }

  rawTable.Rows.Add(row);
}

private static Contact GetContact(Guid contactId, string[] facets)
{
  using (var client = SitecoreXConnectClientConfiguration.GetClient())
  {
    var contactReference = new ContactReference(contactId);
    var contact = facets == null || facets.Length == 0 ? client.Get(contactReference, new ContactExpandOptions(Array.Empty<string>())) : client.Get(contactReference, new ContactExpandOptions(facets));
    if (contact == null)
    {
      throw new ContactNotFoundException(FormattableString.Invariant($"No Contact with id [{contactId}] found"));
    }
   
  return contact;
  }
}

When comparing this code to what we had in Sitecore 8, we have a bit more code but it all seems to make sense. Especially when  you get familiar with coding with xConnect.
What happens here in short:
  1. Create a dataTable
  2. Fetch the contact from xConnect with the necessary facet(s)
  3. Read the data from the contact and add it to the dataTable
  4. Set the dataTable in the pipeline arguments

Special thanks to Nico Geeroms who did the actual coding and testing on this one.

No comments:

Post a Comment