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:
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:
- Introducing contact facets by Adam Conn
- Sitecore 8 xDB and Experience Profile Simplified by Ian Graham
