Sitecore contact facets – Create your own facet

This article describes how to create a simple Sitecore facet consisting of a DateTime and a list of strings.

A contact is made up of facets. Here are all the facets Sitecore uses (you will find the facets in \App_Config\Include\Sitecore.Analytics.Model.Config):

<facets>
  <facet name="Personal" contract="Sitecore.Analytics.Model.Entities.IContactPersonalInfo, Sitecore.Analytics.Model" />
  <facet name="Addresses" contract="Sitecore.Analytics.Model.Entities.IContactAddresses, Sitecore.Analytics.Model" />
  <facet name="Emails" contract="Sitecore.Analytics.Model.Entities.IContactEmailAddresses, Sitecore.Analytics.Model" />
  <facet name="Phone Numbers" contract="Sitecore.Analytics.Model.Entities.IContactPhoneNumbers, Sitecore.Analytics.Model" />
  <facet name="Picture" contract="Sitecore.Analytics.Model.Entities.IContactPicture, Sitecore.Analytics.Model" />
  <facet name="Communication Profile" contract="Sitecore.Analytics.Model.Entities.IContactCommunicationProfile, Sitecore.Analytics.Model" />
  <facet name="Preferences" contract="Sitecore.Analytics.Model.Entities.IContactPreferences, Sitecore.Analytics.Model" />
</facets>

In this example I will add a facet that consists of a date and a list of strings. I will call it “AvailablePublishers“.

This is a real-life example where I needed to store a list of publishers that were available the last time the user was online. Each publisher is just an ID (a string) and I store these as a list on the Contact:

Available Publishers Facet

Available Publishers Facet

It sounds simple, and it is – but there is a lot of code involved. So hang on, lets code.

STEP 1: THE BASIC INTERFACES

The “AvailablePublishers” is a Facet, the list below consists of Elements. So I need to create a IFacet interface and a IElement interface.

Here is the IFacet:

using System;
using Sitecore.Analytics.Model.Framework;

namespace PT.AvailablePublishers
{
  public interface IAvailablePublishersFacet : IFacet
  {
    IElementCollection<IAvailablePublishersElement> AvailablePublishers { get; }
    DateTime Updated { get; set; }
  }
}

The IFacet contains a list (IElementCollection) of my IElement. Here is the IElement:

using Sitecore.Analytics.Model.Framework;

namespace PT.AvailablePublishers
{
  public interface IAvailablePublishersElement : IElement, IValidatable
  {
    string PublisherID { get; set; }
  }
}

STEP 2: THE IMPLEMENTATION

Now we need concrete classes implementing IAvailablePublishersFacet and IAvailablePublishersElement:

Here is the AvailablePublishersFacet class:

using System;
using Sitecore.Analytics.Model.Framework;

namespace PT.AvailablePublishers
{
  [Serializable]
  public class AvailablePublishersFacet : Facet, IAvailablePublishersFacet
  {
    public static readonly string _FACET_NAME = "AvailablePublishers";
    private const string _UPDATED_NAME = "LastUpdated";

    public AvailablePublishersFacet()
    {
      EnsureCollection<IAvailablePublishersElement>(_FACET_NAME);
    }

    public IElementCollection<IAvailablePublishersElement> AvailablePublishers
    {
      get
      {
        return GetCollection<IAvailablePublishersElement>(_FACET_NAME);
      }
    }


    public DateTime Updated
    {
      get
      {
        return GetAttribute<DateTime>(_UPDATED_NAME);
      }
      set
      {
        SetAttribute(_UPDATED_NAME, value);
      }
    }
  }
}

and the AvailablePublishersElement class:

using System;
using Sitecore.Analytics.Model.Framework;

namespace PT.AvailablePublishers
{
  [Serializable]
  public class AvailablePublishersElement : Element, IAvailablePublishersElement
  {
    private const string _PUBLISHERID = "PublisherID";

    public AvailablePublishersElement()
    {
      EnsureAttribute<string>(_PUBLISHERID);
    }
    
    public string PublisherID
    {
      get
      {
        return GetAttribute<string>(_PUBLISHERID);
      }
      set
      {
        SetAttribute(_PUBLISHERID, value);
      }
    }
  }
}

Both classes are serializable.

Getting and setting properties are done using GetAttribute and SetAttribute methods retrieved from Sitecore.Analytics.Model.Framework.Element and Sitecore.Analytics.Model.Framework.Facet.

Lists are done using IElementCollection or IElementDictionary, provided by Sitecore.

STEP 3: REGISTER FACET IN SITECORE CONFIGURATION

The facets and elements are registered in Sitecore. In the configuration you also register the  IAvailablePublishersFacet as an element, even when it inherits from IFacet.

This is a simplified version of the \App_Config\Include\Sitecore.Analytics.Model.config where I have removed all other elements but my own:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <model>
      <elements>
        <element interface="PT.AvailablePublishers.IAvailablePublishersElement, MyDLL" implementation="PT.AvailablePublishers.AvailablePublishersElement, MyDLL" />
        <element interface="PT.AvailablePublishers.IAvailablePublishersFacet, MyDLL" implementation="PT.AvailablePublishers.AvailablePublishersFacet, MyDLL" />
      </elements>
      <entities>
        <contact>
          <facets>
            <facet name="AvailablePublishers" contract="PT.AvailablePublishers.IAvailablePublishersFacet, MyDLL" />
          </facets>
        </contact>
      </entities>
    </model>
  </sitecore>
</configuration>

So as you can see, both my interfaces are defined in <elements/>. The <elements/> describes the implementation of the interface, just like IOC would do it.

The facet is defined in <facets/> with a unique name. This unique name is the name you use to find the facet when you need to access it.

STEP 4: CALL THE FACET

The facet is now defined. Next step is to use the facet. To retrieve a facet you need a Contact. The contact is usually retrieved from the Sitecore Tracker:

Contact contact = Tracker.Current.Session.Contact;

This is a repository than can get the list of available publishers from a contact, and update the list of available publishers:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using PT.AvailablePublishers;
using Sitecore.Analytics.Tracking;

namespace PT.Forum.Domain.NejTakPlus.Model.Repositories
{
  public class AvailablePublishersRepository
  {
    public IEnumerable<string> Get(Contact contact)
    {
      IAvailablePublishersFacet facet = contact.GetFacet<IAvailablePublishersFacet>(AvailablePublishersFacet._FACET_NAME);
      return facet.AvailablePublishers.Select(element => element.PublisherID);
    }

    public void Set(Contact contact, IEnumerable<string> availablePublisherKeys)
    {
      IAvailablePublishersFacet facet = contact.GetFacet<IAvailablePublishersFacet>(AvailablePublishersFacet._FACET_NAME);
      facet.Updated = DateTime.Now;
      while (facet.AvailablePublishers.Count > 0)
        facet.AvailablePublishers.Remove(0);
      foreach (string availablePublisherKey in availablePublisherKeys)
      {
        facet.AvailablePublishers.Create().PublisherID = availablePublisherKey;
      }
    }
  }
}

Notice the contact.GetFacet<>() method. Here you specify the name of the facet you defined in the <facets/> section of the config file. Luckily you also define the same name in the code, so I can get the facet name from my AvailablePublishersFacet class.

Also notice the Set() method. If you wish to clear the list before inserting, you need to iterate through the list and remove them one by one. There is no Clear() method.

Remember that facets are not written to MongoDB before your session ends. So you cannot see your fact in Mongo before your session is abandoned. You can try calling:

Session.Abandon();

To force Sitecore to write the data to MongoDB.

That’s it. Happy coding.

MORE TO READ:

 

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in .net, c#, General .NET, Sitecore 8 and tagged , , , , , , , , . Bookmark the permalink.

12 Responses to Sitecore contact facets – Create your own facet

  1. timgriff84 says:

    Reblogged this on hi my name is tim.

  2. Dave says:

    Brian, do you have an entry that shows how to display this custom data on the Contact card page?

  3. briancaos says:

    No I haven’t made a new Speak part for displaying my new data. Maybe in a later post.

  4. Gavin Knights says:

    Great post Brian, one question though. Does your custom data persist ok if you read back the Contact using Tracker.Current.Session.Identify? My custom data seems to be erased when I read back the Contact on the next visit.

  5. briancaos says:

    Custom data are stored in MongoDB when the user session expires. So you cannot see your data in MongoVue before the session expires.
    But you should be able to see the data when requesting the contact from Tracker.Current.Serssion.Identify, because the data is stored in the session.

    There is a bug running around at the moment where if you store data in a session-less environment (from a stateless API or from a webservice) that the Shared Session manager does not always have a success locking the contact. When this happens, it reads old data from MongoDB rather than throwing an exception.

    If you use sessions, there should be no problem.

  6. SC_LEF says:

    Brian, this is a very useful post. Thanks very much.
    Does creating our own facet depend on if we are running xDB in the cloud or on-premises?

  7. briancaos says:

    Not that I am aware of

  8. Pingback: Sitecore Contacts – Create and save contacts directly to and from xDB (MongoDB) | Brian Pedersen's Sitecore and .NET Blog

  9. Pingback: Sitecore List Manager – Add Contacts to EXM Lists | Brian Pedersen's Sitecore and .NET Blog

  10. It is unclear to me exactly how to use the IElementCollection.Create method. In your example you have a simple list of strings so you can do facet.AvailablePublishers.Create().PublisherID = availablePublisherKey;. But what if I have a list of more complex objects instead of just strings? How would I create a complex object and set a number of properties?

  11. Pingback: Sitecore MongoDB – What do we know about a contact? | Brian Pedersen's Sitecore and .NET Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s