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

Creating an automated online voting platform for seamless onboarding and improved Voter Experience

One of the major challenges faced by the customer was - conducting the cooperative elections amidst the nationwide lockdowns in the US as casting votes on a piece of paper was neither safe nor feasible. Espire built an online voting application to integrate all the three existing ways of voting in one place including: Online voting (through a website), SmartHub voting (through single sign-on), Paper ballot voting (which includes data entry and verification)

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

Digital Communications and CCM Solutions 4 key initiatives to Improve Customer Experience

Digital communications help in stitching your prospect to customer journeys together by keeping them actively engaged and allowing them to re-initiate conversation from where they left off. Delivering contextual and interactive communications can help the customers take the next step and reach out to you through the channel convenient for them.

Total experience digital transformation strategy for global businesses

The Total Experience strategy has emerged as a key trend for businesses to drive better digital experiences for users, customers and employees while improving business growth

Digital experience management 5 best practices to improve customer experience on b2b websites

Customer Experience is at the heart of customer service and building long-lasting customer relationships. Customer experience will emerge as the key determinant of business success, surpassing the quality and price of products. While B2C brands have prioritized customer experience as a strategic business goal, B2B companies are yet to navigate their technology investments towards digital experience management.

Sitecore cdp optimization 5 steps for deploying customer data platform

The Customer Data Platform (CDP) has become a critical part of the modern technology stack, as a one-stop solution for data orchestration and management. Sitecore CDP comprises of all the core data management capabilities along with intelligent decisioning, predictive analytics, experimentation, and orchestration for driving positive brand experiences

Subscribe To Our Blog

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