Author : Vikas Kumar
Director - Customer Experience, Espire | Sitecore MVP 2021
03/Feb/2019

Vikas is a Sitecore MVP who heads the Digital Experience Platform (DXP) initiatives at Espire. A skilled craftsman in competency building, Vikas is a go-to-CMS expert, known for designing & developing best practices, that consistently transforms digital journey for clients.


Driving personalization with xConnect APIs and custom facet data | Part 1

Introduction

Personalization aims at delivering the right content to the right customer. The moment a user interacts with any of your systems, you are presented with a significant amount of data that can be utilized to deliver a moderately tailored and personalized experience to your visitor. The content can be significantly improved further once the user is identified and more is known about his preferences.

Sitecore xConnect provides the capability to capture data about a user from various sources and helps integrate everything into XDB for us to leverage.

The blog explains how custom data from external sources can be captured and used to personalize content created in Sitecore to improve the overall customer experience.

Follow our session on YouTube

Creating a Custom Facet and Model

1. In order to capture custom data about a contact, we first need to define a model that would hold the new information. We do so by creating a class with its properties resenting the data to be captured and inheriting the class Sitecore.XConnect.Facet. We also define a Default Facet Key which would later help us consult the facet using the API.


 [FacetKey(DefaultFacetKey)] 
    public class StudentInfo : Facet 
    { 
        public const string DefaultFacetKey = "StudentInfo"; 
        public string Courses { get; set; } 
        public string Country { get; set; } 
    } 

2. The next step is to create a custom collection model. A collection model defines what data is collected for a contact and stored in XDB. This model is used in extension with the default Core Collection Model, which defines the basic information such as First Name, Email etc., collected in XDB by default.


 public  class SitecoreStudentCustomModel 
    { 
        public static XdbModel Model { get; } = BuildModel(); 
        private static XdbModel BuildModel() 
        { 
            XdbModelBuilder modelBuilder = new XdbModelBuilder("SitecoreCustomModel", new XdbModelVersion(1, 0)); 
            modelBuilder.DefineFacet<contact,>(FacetKeys.StudentInfo); 
            modelBuilder.ReferenceModel(Sitecore.XConnect.Collection.Model.CollectionModel.Model); 
            return modelBuilder.BuildModel(); 
        } 
    } 

    public class FacetKeys 
    { 
        public const string StudentInfo = "StudentInfo"; 
    } 

3. The next step is to deploy the new model in the form of a JSON file to xConnect roles. In order to do so, create a console application and run the following code.


 class Program 
        { 
            static void Main(string[] args) 
            { 
                var model = SCM.SitecoreStudentCustomModel.Model; 
                var serializedModel = Sitecore.XConnect.Serialization.XdbModelWriter.Serialize(model); 
                File.WriteAllText("D:\\ImportCSV\\files\\" + model.FullName + ".json", serializedModel); 
            } 
        } 

4. Copy the created model JSON file to the following paths:


C:\\root\App_Data\Models 
C:\\root\App_data\Models 
C:\\root\App_Data\Models 


Importing Data into XDB

For demo purpose, we created a custom console application which would import contact information into XDB from a CSV file. A google form was created to capture the relevant information and the list of submissions were converted to a CSV file and used to create contacts.

1. Create method to load CSV file data in model


//load csv data to student list 
        public static List LoadCSV(string filePath) 
        { 
            try 
            { 
                var listStudent = (from row in File.ReadAllLines(filePath).Skip(1) 
                                   let studentColData = row.Split(',') 
                                   select new StudentData 
                                   { 
                                       StudentName = studentColData[1], 
                                       Courses = studentColData[3], 
                                       Country= studentColData[4], 
                                       Email= studentColData[2], 
                                       Uid= studentColData[0] 
                                   }).ToList(); 
                return listStudent; 
            } 
            catch (Exception ex) { System.IO.File.AppendAllText(ConfigHelper.getConfigValue("LogFilePath"), DateTime.Now.ToString() + ": " + ex.ToString() + "\n"); return null; } 
        } 


2. Create method to load xConnect configuration for the API

This method would contain logic to load the new custom collection model when getting contact data from XDB


//set the Xconnect configuration 
        public static async Task GetXConnectConfiguration() 
        { 
            CertificateWebRequestHandlerModifierOptions options = 
            CertificateWebRequestHandlerModifierOptions.Parse(CWRHMO); 
            var certificateModifier = new CertificateWebRequestHandlerModifier(options); 
            List clientModifiers = new List(); 
            var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20)); 
            clientModifiers.Add(timeoutClientModifier); 
            var collectionClient = new CollectionWebApiClient(new Uri(ConfigHelper.getConfigValue("CollectionWebApiClient")), clientModifiers, new[] { certificateModifier }); 
            var searchClient = new SearchWebApiClient(new Uri(ConfigHelper.getConfigValue("SearchWebApiClient")), clientModifiers, new[] { certificateModifier }); 
            var configurationClient = new ConfigurationWebApiClient(new Uri(ConfigHelper.getConfigValue("ConfigurationWebApiClient")), clientModifiers, new[] { certificateModifier }); 
            XdbModel[] models = { CollectionModel.Model, SitecoreStudentCustomModel.Model }; 
            var cfg = new XConnectClientConfiguration( 
            new XdbRuntimeModel(models), collectionClient, searchClient, configurationClient); 

            try 
            { 
                await cfg.InitializeAsync(); 
                return cfg; 
            } 
            catch (XdbModelConflictException ce) 
            { 
                System.IO.File.AppendAllText(ConfigHelper.getConfigValue("LogFilePath"), DateTime.Now.ToString() + ": " + ce.ToString() + "\n"); 
                return null; 
            } 
        } 

3. Create method to Import data in XDB

This would create contacts based on the information loaded from CSV and defines what data is stored in each facet.


//Import csv data to XDB 
        private static async Task ExportToXDB(List students) 
        { 
            try 
            { 
                var offlineGoal = Guid.Parse(ConfigurationManager.AppSettings.Get("OfflineGoal"));  
                var channelId = Guid.Parse(ConfigurationManager.AppSettings.Get("ChannelId")); 
                var source = "Uid"; 
                var cfg = await GetXConnectConfiguration(); 
                using (var client = new XConnectClient(cfg)) 
                { 
                    foreach (var objStudent in students) 
                    { 
                        // to identifier the student by uid 
                        IdentifiedContactReference reference = new IdentifiedContactReference(source, objStudent.Uid); 
                        Contact existingContact = await client.GetAsync(reference, new ContactExpandOptions(new string[] { PersonalInformation.DefaultFacetKey, EmailAddressList.DefaultFacetKey, StudentInfo.DefaultFacetKey })); 
                        if (existingContact != null) 
                        { 
                            PersonalInformation existingContactPersonalFacet = existingContact.GetFacet(PersonalInformation.DefaultFacetKey); 
                            if (existingContactPersonalFacet != null) 
                            { 
                                existingContactPersonalFacet.FirstName = objStudent.StudentName; 
                                existingContactPersonalFacet.Nickname = objStudent.Country; 
                                existingContactPersonalFacet.JobTitle = objStudent.Courses; 
                                client.SetFacet(existingContact, PersonalInformation.DefaultFacetKey, existingContactPersonalFacet); 
                            } 
                            else 
                            { 
                               PersonalInformation personalInfoFacet = new PersonalInformation(); 
                               personalInfoFacet.FirstName = objStudent.StudentName; 
                               client.SetFacet(existingContact, PersonalInformation.DefaultFacetKey, personalInfoFacet); 
                            } 

                            EmailAddressList existingContactEmailFacet = existingContact.GetFacet(EmailAddressList.DefaultFacetKey); 
                            if (existingContactEmailFacet != null) 
                            { 
                                existingContactEmailFacet.PreferredEmail = new EmailAddress(objStudent.Email, true); 
                                existingContactEmailFacet.PreferredKey = "work"; 
                                client.SetFacet(existingContact, EmailAddressList.DefaultFacetKey, existingContactEmailFacet); 
                            } 

                            else 
                            { 
                                EmailAddressList emailAddressList = new EmailAddressList(new EmailAddress(objStudent.Email, true), "work"); 
                                client.SetFacet(existingContact, EmailAddressList.DefaultFacetKey, emailAddressList); 
                            } 

                            StudentInfo existingStudentInfoFacet = existingContact.GetFacet(StudentInfo.DefaultFacetKey); 
                            if (existingStudentInfoFacet != null) 
                            { 
                                existingStudentInfoFacet.Uid = objStudent.Uid; 
                                client.SetFacet(existingContact, StudentInfo.DefaultFacetKey, existingStudentInfoFacet); 
                            } 

                            else 

                            { 
                                StudentInfo stuudentInfo = new StudentInfo() { Country = objStudent.Country, Courses = objStudent.Courses }; 
                                client.SetFacet(existingContact, StudentInfo.DefaultFacetKey, stuudentInfo); 
                            } 

                            // Create a new interaction for that contact 

                            Interaction interaction = new Interaction(existingContact, InteractionInitiator.Brand, channelId, ""); 

                           // Add events - all interactions must have at least one event 

                            var xConnectEvent = new Goal(offlineGoal, DateTime.UtcNow); 
                            interaction.Events.Add(xConnectEvent); 

                            // Add the contact and interaction 

                           client.AddInteraction(interaction); 
                        } 

                       else 
                        { 

                            // Identifier for a 'known' contact 

                            var identifier = new ContactIdentifier[] 
                           { 
                                    new ContactIdentifier(source, objStudent.Uid, ContactIdentifierType.Known) 
                            }; 

 

                            // Create a new contact with the identifier 

                            Contact knownContact = new Contact(identifier); 
                            PersonalInformation personalInfoFacet = new PersonalInformation(); 
                            personalInfoFacet.FirstName = objStudent.StudentName; 
                            client.SetFacet(knownContact, PersonalInformation.DefaultFacetKey, personalInfoFacet); 
                            EmailAddressList emailAddressList = new EmailAddressList(new EmailAddress(objStudent.Email, true), "work"); 
                            client.SetFacet(knownContact, EmailAddressList.DefaultFacetKey, emailAddressList); 
                            StudentInfo studentInfo = new StudentInfo() { Country = objStudent.Country, Courses = objStudent.Courses}; 
                            client.SetFacet(knownContact, StudentInfo.DefaultFacetKey, studentInfo); 
                            client.AddContact(knownContact); 


                            // Create a new interaction for that contact 

                            Interaction interaction = new Interaction(knownContact, InteractionInitiator.Brand, channelId, ""); 


                            // Add events - all interactions must have at least one event 

                            var xConnectEvent = new Goal(offlineGoal, DateTime.UtcNow); 
                            interaction.Events.Add(xConnectEvent); 


                            // Add the contact and interaction 

                           client.AddInteraction(interaction); 
                        } 


                        // Submit contact and interaction - a total of two operations 

                        await client.SubmitAsync(); 
                        Console.WriteLine(objStudent.Email); 
                        Console.WriteLine(objStudent.StudentName + "UID:" + objStudent.Uid); 

                        // Get the last batch that was executed 

                        var operations = client.LastBatch; 
                        Console.WriteLine("RESULTS..."); 

                        // Loop through operations and check status 

                        foreach (var operation in operations) 
                        { 
                            Console.WriteLine(operation.OperationType + operation.Target.GetType().ToString() + " Operation: " + operation.Status); 
                        } 
                    } 
                } 
                return true; 
            } 
            catch (Exception ex) 
            { 
                System.IO.File.AppendAllText(ConfigHelper.getConfigValue("LogFilePath"), DateTime.Now.ToString() + ": " + ex.ToString() + "\n"); 
                return false; 
            } 
        } 


4. Call the methods created above for execution


 private static void Main(string[] args) 
        { 
            string filePath = ConfigurationManager.AppSettings.Get("csvPath"); 
            if (IsFileExists(filePath)) 
            { 
                ExportToXDB(LoadCSV(filePath)).ConfigureAwait(false).GetAwaiter().GetResult(); 
            } 
            else 
            { 
               Console.WriteLine("File doesn't exists"); 
            } 
          } 


After this step, you can verify the contacts created in the Experience Profile application in Sitecore. This concludes the steps required to capture custom data in XDB using XConnect APIs.

Follow part 2 of the blog to see how to use this custom data to personalize content using custom personalization rules.

MORE FROM OUR BLOGS

Designed a Centralized Content Management System for a Leading UK-based Staffing Business

A leading Global Talent Acquisition and Managed Workforce Solutions provider in the UK, needed support to centralize its content management for the 22+ acquired brands and required a simplified process for posting job openings & deploying regular enhancements for content management. Espire considering the challenges, reimplemented the front-end design framework for better performance, security and SEO optimization. We also redesigned the cloud architecture on Microsoft Azure to support constant enhancements required to centralize & manage enterprise & localized content. 

Design & Development of a Responsive SharePoint Website For the holding company of Singapore’s public healthcare providers

The client manages the public healthcare system to ensure that good and affordable basic medical services are available to all Singaporeans.

Enhanced digital customer experiences for a leading Singapore headquartered transshipment hub

The customer's existing website was deployed on-premises and leveraged a WCM platform, Joomla they required a strategic technology partner to revamp the website, improve content delivery and website navigation with needed to map customer journey and access visitor analytics efficiently

Accelerating digital experiences with powerful ui ux for leading industries

UI & UX has emerged as a mainstay when it comes to offering an engaging and easy-to-navigate website/user interface to users. A careful UI/UX design strategy can help in transforming the way organizations interact with their customers as well as their employees by making the platform more streamlined, agile, proactive, easy-to-use and intuitive. Moreover, it can help in increasing user retention, engagement, conversion rates and eventually better ROI.

Espire infolabs enhances search experience for acu wins searchstax partner excellence award 2021

The global leader in search experience management, SearchStax has recognized Espire Infolabs in the Partner Excellence Award 2021 (APAC) for assisting Australian Catholic University (ACU), transform its site search with SearchStudio and driving greater engagement and conversions.

Sitecore composable dxp multiexperience solutions for driving greater business outcomes

Espire is helping leading brands deploy Sitecore Composable DXP to deliver multiexperience and total experience solutions at scale. Sitecore is constantly adding cutting-edge tools and technologies to give marketers and developers the flexibility to compose their DXP platform to deliver total experience solutions at scale. Enterprises are prioritizing their move towards delivering multiexperience solutions to drive impeccable staff and customer experience.

Sitecore ordercloud top features and benefits for driving exceptional digital experiences

Sitecore’s OrderCloud is an API-first headless cloud platform which empowers brands to streamline key business operations like efficient order management, marketplace application integration and driving eCommerce experiences for customers. It seamlessly integrates with your brand’s existing technology stack to drive targeted customer communications and digital experiences across multiple touchpoints

Subscribe To Our Blog

By clicking on "SUBSCRIBE NOW" you acknowledge having read our Privacy Notice.