LibrarySites.Banner

Introducing Contact Facets

Starting with xDB an individual visitor is called a contact. Contact data is stored in "facets" (which I will call "contact facets" in order to avoid confusion with other kinds of facets).

This post introduces some basic concepts that you need to understand in order to work with contacts and contact facets. It also includes a step-by-step guide on how to read and write custom contact data using contact facets.

Fundamentals

A contact is an individual visitor. This visitor may be anonymous or he may have been authenticated. Either way, Sitecore allows you to persist data about the contact. Contact facets are how you do this.

When you use contact facets to store visitor data you are able to focus on what data you want to store. Sitecore handles writing the data to permanent storage, reading the data from permanent storage, ensuring the data is matched to the correct contact, making the data available when it is needed and so on.

Create Visual Studio Project

If you are following along you need to start with a new Visual Studio project.

  1. Create a new Visual Studio project named Testing.ContactData.
  2. Add references to the following assemblies:
    • Sitecore.Kernel.dll
    • Sitecore.Analytics.dll
    • Sitecore.Analytics.Model.dll
  3. Add the following config file: App_Config/Include/Testing.ContactData.config

Defining a Contact Facet

Before I can store custom visitor data I need to define the contact facet. This involves specifying several things:

  1. the interface that represents the data you want to store
  2. the type that implements that interface
  3. the name that you want to use to retrieve the contact facet data

Step 1 - Defining the Interface

My custom interface must inherit from Sitecore.Analytics.Model.Framework.IFacet.

Add the following interface to your project:

using System;
using System.Linq;
using Sitecore.Analytics.Model.Framework;
 
namespace Testing.ContactFacets.Model
{
    public interface IEmployeeData : IFacet
    {
        string EmployeeId { get; set; }
    }
}

Step 2 - Implementing the Interface

I need a class that implements the contact facet interface. The IFacet interface has a number of members that must be implemented. These members ensure that the data can be converted into a format that can be written to permanent storage (currently MongoDB).

Fortunately Sitecore provides a base implementation that you can use. This simplifies my code, but it also creates some new conditions that my code must meet.

First of all, instead of creating local variables to store the object data I use an API. It's not creating more code, really, it's just creating different code.

Secondly, the kinds of data I can store on the contact is limited. This isn't as bad as it sounds. In a future blog post I will explain these limitations and how, by NOT using the base IFacet implementation, I can store any kind of data I want in the contact. But for now, I want to keep things simple so I am going to limit my data to value types.

Add the following class to the Visual Studio project:

using System;
using System.Linq;
using Sitecore.Analytics.Model.Framework;
 
namespace Testing.ContactFacets.Model
{
    public class EmployeeData : Facet, IEmployeeData
    {
        private const string FIELD_EMPLOYEE_ID = "EmployeeId";
        public EmployeeData()
        {
            base.EnsureAttribute<string>(FIELD_EMPLOYEE_ID);
        }
        public string EmployeeId
        {
            get { return base.GetAttribute<string>(FIELD_EMPLOYEE_ID); }
            set { base.SetAttribute<string>(FIELD_EMPLOYEE_ID, value); }
        }
    }
}

The "EnsureAttribute" method is the equivalent of declaring a value-type variable. The "GetAttribute" method is used to read the value of the variable. The "SetAttribute" method is used to set the value of the variable.

If you look at the Sitecore.Analytics.Model.Framework.Facet type you will see that there are other "Ensure", "Get" and "Set" methods. These are used to handle reference-type objects. I will cover these in a future post.

Step 3 - Registering the Contact Facet

I need to add my contact facet to Sitecore's configuration. This creates a couple of mappings:

  1. The first mapping is from the interface that defines my facet to the type that implements the interface.
  2. The second mapping is from the name I want to use to access the contact facet data to the contact facet interface.

Add the following code to the Testing.ContactFacets.config file:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <model>
      <elements>
        <element interface="Testing.ContactFacets.Model.IEmployeeData, Testing.ContactFacets" implementation="Testing.ContactFacets.Model.EmployeeData, Testing.ContactFacets" />
      </elements>
      <entities>
        <contact>
          <facets>
            <facet name="Employee Data" contract="Testing.ContactFacets.Model.IEmployeeData, Testing.ContactFacets" />
          </facets>
        </contact>
      </entities>
    </model>
  </sitecore>
</configuration>

Step 4 - Deploy the Contact Facet

I need to compile my code and deploy my assembly and config file to my Sitecore server.

Reading and Writing Contact Facet Data

Now that my contact facet is deployed I need to consider how to read and write contact facet data. This is accomplished using the Sitecore Contact API.

I added the following web form page to my Sitecore server:

<%@ Page Language="c#" %>
<%@ Import Namespace="Sitecore.Analytics" %>
<%@ Import Namespace="Testing.ContactFacets.Model" %>
<!DOCTYPE html>
<html>
  <head>
    <title>Add Employee Data</title>
  </head>
  <body>
    <%
      var contact = Tracker.Current.Contact;
      var data = contact.GetFacet<IEmployeeData>("Employee Data");
      data.EmployeeId = "ABC123";
    %>
    <p>Employee data contact facet updated.</p>
    <p>Contact ID: <b><%=contact.ContactId.ToString()%></b></p>
    <p>Employee #: <b><%=data.EmployeeId%></b></p>
  </body>
</html>

Confirming the Code Works

After I run my web form page I get a message on the screen that says the employee number was set. How do I know this is true?

Well, I can create another web form page that only reads the employee number. That will show me that the contact facet data is being stored in memory at least. But what about permanent storage?

For performance reasons Sitecore only writes contact data to xDB when the session ends. This means that if I look in MongoDB for my custom data, I will not see that data until the session ends. You can use the following web form page to end the session:

<%@ Page language="c#" %>
<script runat="server">
  void Page_Load(object sender, System.EventArgs e) {
      Session.Abandon();
  }
</script
<!DOCTYPE html>
<html>
  <head>
    <title>Session Abandon</title>
  </head>
  <body>
  </body>
</html>

After the session ends I can look in MongoDB and find my custom data. I prefer to use RoboMongo to look at MongoDB data. It's a free download available at http://robomongo.org/.

Using RoboMongo I can open the analytics database and display the contents of the Contacts collection. And I can see that my custom contact facet data has been recorded.

Sitecore Installation Package & Source Code

Next Steps

This covers the basics aspects of getting custom contact data into xDB. In my next post I will start to cover some of the different ways this data can be used.

  • Could this be used to replace the slow .NET User Profile (reading and writing through User.Profile.GetCustomProperty()/SetCustomProperty is rather slow in a 300.000+ user database)?  I am worried about the writing. If you only update xDB on a session end, other servers will not be able to see the updated properties when running a staged environment?  Also, is it possible to couple a user profile with the facets without having a session, so facets could be used in rules, emails and engagement plans?

  • Hi Brian,  Yes, this can replace the user profile. Profile data can be written to the contact. The data can be read using the search engine (see www.sitecore.net/.../Using-Custom-Contact-Data-Part-2-Search.aspx for an explanation).  If your session state is inproc there would be a problem in multi-server environment, but you can use the Mongo or SQL Server session state providers to make the session available across the entire environment.  Facets can be used in rules, emails, etc. I'm working on a blog post that demonstrates this.

  • I Adam.  How would you recommend using contacts in an enviroment in which the session is not always present? If the contact is accessed via a stateless REST API, how would you get/update the contact and still making sure that the data is available if the user should choose to access the data via the website shortly after?  The scenario:  1. The user is updating his/her name via an app that communicates with sitecore through a session-less Web Api 2. The user logs-on to the website shortly after and expects to see updated data 3. The user updates the his/her name via the web.

  • We have a multi server environment and I'm having some real trouble getting this working(stackoverflow.com/.../can-i-still-access-a-sitecore-contact-facet-once-session-is-flushed). Is the facet stored in session before writing to the mongoDb? I'm reading between the lines on the Session.Abandon() bit.

  • In our project, we have a requirement to perform item search by using custom facets in sitecore.   It would be greatly appreciated if you can share us some thoughts on how to add custom facets in sitecore, re-index them and how to integrate them in Coveo to perform searches.  Thanks in advance!