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

Delivering Consistent And Compelling Customer Communication With Self Serve Communication Portal

Delivering Consistent And Compelling Customer Communication With Self Serve Communication Portal

Supporting & Building Digital Solutions for Improving Customer Experience

Espire upgraded their existing corporate website to Sitecore version 9.1 for streamlining navigation issues, upgraded their E-commerce platform for delivering personalized customer experiences, deployed Sitecore technical enhancements to improve development process like refactoring, Unicorn, Glass mapper and T4templates, Search etc

A Leading Offender Management Software Solution Provider Gains the Competitive Edge of Agility

The client is a leading provider of offender management product, catering to the Governments sector mainly to Canada, USA and UK. With the aim of becoming agile by automating the internal processes and modernizing the systems, the client decided to develop an application to assess their end-client’s business process environment.

Powering Digital Transformation by revamping Quote & Buy process for a UK based leading Insurance company

Espire enhanced the existing Digital Transformation Journey using Umbraco CMS,Designed and implemented Umbraco based system for content management of the forms and modules

A Complete guide to upgrade to sitecore xm cloud why when and how

Sitecore XM Cloud's composable architecture allows organizations to create and manage personalized digital experiences for their customers, increasing engagement and conversions. With its cloud-based delivery model, Sitecore XM Cloud eliminates the need for organizations to maintain on-premises infrastructure, providing a cost-effective and secure solution for managing digital customer experiences

Leveraging composable architecture to drive unforgettable digital experiences

A composable architecture allows the application to adapt to the speed of business while enhancing responsiveness and scalability, paving the way towards driving greater business growth. In our latest blog, we share the benefits of leveraging a composable microservices-led architecture for business applications.

Top 10 strategic technology trends for 2023 by gartner Part2

In our first blog on Top 10 Strategic Technology Trends for 2023 by Gartner, we had discussed the top 5 technology trends that will enable businesses to offer customized experiences to the end users for better engagement, ROI, conversion, and sales. As a concluding part of this 2 series blog, we will enlist and discuss the next 5 disruptive tech trends to be witnessed in 2023 such as Wireless value realization, Superapps, Adaptive AI, Metaverse and Sustainable technology.

Top 10 strategic technology trends for 2023 by gartner Part1

The pace of change is accelerating, driven by emerging technologies such as artificial intelligence, the Internet of Things, big data and blockchain-all of which are changing how we work, live, and manage our businesses. In this blog, Part 1 of the top 10 strategic technology trends for 2023, we will be exploring the top 5 trends identified by Gartner, which can significantly contribute to your success story.

Subscribe To Our Blog

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

global-locations

12

+

Global Locations

global-services

100

+

Global Customers Served

resources-certified

1200

+

Resources

technology-partnerships

30

+

Technology Partnerships

years-of-experience

20

+

Years of Experience in Digital Transformation & Total Experience

Speak to Our Digital Transformation Specialists

Let's get you started on the digital-first & transformation journey. Reserve your free consultation or a demo today!