Walkthrough: Creating custom facets and events

Abstract

How to create custom facets and events with Sitecore xConnect.

This walkthrough is part five of the Extending the xConnect collection model walkthrough series. To complete this walkthrough, you must first complete walkthroughs part one, two, three, and four.

In this walkthrough, you are Sitecore Cinema, and you must extend the collection model to collect information from your visitors. The information relies on visitors swiping their loyalty card each time they interact with one of your cinemas, and as an incentive to visit, you offer free popcorn after 10 uses of the loyalty card.

The walkthrough describes how to:

  • Create the SitecoreCinema solution and class library project

  • Create the CinemaInfo interaction facet class

  • Create the CinemaVisitorInfo contact facet class

  • Create the WatchMovie outcome item

  • Create the WatchMovie outcome class

  • Create the BuyConcessions outcome item

  • Create the BuyConcessions outcome class

  • Create the UseSelfService event item

  • Create the UseSelfService event class

  • Create the SitecoreCinemaModel collection model class

  • Create the Console.Deploy console app project

  • Serialize SitecoreCinemaModel collection model

  • Create the Console.Journey console app project

  • Simulate a Sitecore Cinema visit

Create the SitecoreCinema solution and class library project

To create the SitecoreCinema solution and class library project:

  1. In Visual Studio with administrator privileges, create a new solution and class library project for .NET Framework called SitecoreCinema:

    The Configure your new project dialogue box in Visual Studio.
  2. In Solution Explorer, right-click SitecoreCinema/References and click Add Reference.

  3. In the Reference Manager window, click Browse and add the following references from your <xConnect instance>\bin\ folder:

    • Sitecore.XConnect.Collection.Model.dll

    • Sitecore.XConnect.dll

  4. Click OK.

Create the CinemaInfo interaction facet class

The CinemaInfo class is an interaction facet that contains information about the cinema visited during the interaction.

To create the CinemaInfo interaction facet class:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click the SitecoreCinema project and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C# Items.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter CinemaInfo.cs and click Add.

  3. In the CinemaInfo.cs file, replace the content with the following code:

    using Sitecore.XConnect;
    
    namespace SitecoreCinema.Model.Collection
    {
        [FacetKey(DefaultFacetKey)]
        public class CinemaInfo : Facet
        {
            public const string DefaultFacetKey = "CinemaInfo";
            public int CinemaId { get; set; }
        }
    }
    
  4. Save the CinemaInfo.cs file.

Create the CinemaVisitorInfo contact facet class

The CinemaVisitorInfo class is a contact facet that contains information about the visitor, such as loyalty ID.

To create the CinemaVisitorInfo contact facet class:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click the SitecoreCinema project and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C# Items.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter CinemaVisitorInfo.cs and click Add.

  3. In the CinemaVisitorInfo.cs file, replace the content with the following code:

    using Sitecore.XConnect;
    
    namespace SitecoreCinema.Model.Collection
    {
        [FacetKey(DefaultFacetKey)]
        public class CinemaVisitorInfo : Facet
        {
            public const string DefaultFacetKey = "CinemaVisitorInfo";
            public string FavoriteMovie { get; set; }
        }
    }
    
  4. Save the CinemaVisitorInfo.cs file.

Create the WatchMovie outcome item

To create the WatchMovie outcome item:

  1. In the Marketing Control Panel, click Marketing Control Panel/Outcomes.

  2. In the Options pane, click Outcome Definition.

  3. In the Message window, enter WatchMovie and click OK.

  4. In the Quick Info section, make a note of the item ID.

Create the WatchMovie outcome class

The WatchMovie outcome class triggers the WatchMovie outcome item from the Point-of-Sale system when the visitor buys a movie ticket.

To create the WatchMovie outcome class:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click the SitecoreCinema project and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C# Items.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter WatchMovie.cs and click Add.

  3. In the WatchMovie.cs file, replace the content with the following code:

    using Sitecore.XConnect;
    using System;
    
    namespace SitecoreCinema.Model.Collection
    {
        public class WatchMovie : Outcome
        {
            public static Guid EventDefinitionId { get; } = new Guid("<WATCHMOVIE OUTCOME ITEM ID>");
    
            public WatchMovie(
                DateTime timestamp,
                string currencyCode,
                decimal monetaryValue
            ) : base(EventDefinitionId, timestamp, currencyCode, monetaryValue) { }
    
            // Entertainment Identifier Registry number (see https://eidr.org/)
            public string EIDR { get; set; }
        }
    }
    
  4. Insert the WatchMovie outcome item ID.

  5. Save the WatchMovie.cs file.

Create the BuyConcessions outcome item

To create the BuyConcessions outcome item:

  1. In the Marketing Control Panel, click Marketing Control Panel/Outcomes.

  2. In the Options pane, click Outcome Definition.

  3. In the Message window, enter BuyConcessions and click OK.

  4. In the Quick Info section, make a note of the item ID.

Create the BuyConcessions outcome class

The BuyConcessions outcome class triggers the BuyConcessions outcome item from the Point-of-Sale system when the visitor buys concessions.

To create the BuyConcessions outcome class:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click the SitecoreCinema project and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C# Items.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter BuyConcessions.cs and click Add.

  3. In the BuyConcessions.cs file, replace the content with the following code:

    using Sitecore.XConnect;
    using System;
    
    namespace SitecoreCinema.Model.Collection
    {
        public class BuyConcessions : Outcome
        {
            public static Guid EventDefinitionId { get; } = new Guid("<BUYCONCESSIONS OUTCOME ITEM ID>");
    
            public BuyConcessions(
                DateTime timestamp,
                string currencyCode,
                decimal monetaryValue
            ) : base(EventDefinitionId, timestamp, currencyCode, monetaryValue) { }
    
            public bool BoughtAlcoholicDrink { get; set; }
        }
    }
    
  4. Insert the BuyConcessions outcome item ID.

  5. Save the BuyConcessions.cs file.

Create the UseSelfService event item

To create the UseSelfService event item:

  1. In the Marketing Control Panel, click Marketing Control Panel/Events.

  2. In the Options pane, click Event.

  3. In the Message window, enter UseSelfService and click OK.

  4. In the Quick Info section, make a note of the item ID.

Create the UseSelfService event class

The UseSelfService event class triggers the UseSelfService event item from the ticket machine when the visitor buys a movie ticket.

To create the UseSelfService event class:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click the SitecoreCinema project and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C# Items.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter UseSelfService.cs and click Add.

  3. In the UseSelfService.cs file, replace the content with the following code:

    using Sitecore.XConnect;
    using System;
    
    namespace SitecoreCinema.Model.Collection
    {
        public class UseSelfService : Event
        {
            public static Guid EventDefinitionId { get; } = new Guid("<USESELFSERVICE EVENT ITEM ID>");
            public UseSelfService(DateTime timestamp) : base(EventDefinitionId, timestamp) { }
        }
    }
    
  4. Insert the UseSelfService event item ID.

  5. Save the UseSelfService.cs file.

Create the SitecoreCinemaModel collection model class

To create the SitecoreCinemaModel collection model class:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click SitecoreCinema and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C# Items.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter SitecoreCinemaModel.cs and click Add.

  3. In the SitecoreCinemaModel.cs file, replace the content with the following code:

    using Sitecore.XConnect;
    using Sitecore.XConnect.Schema;
    
    namespace SitecoreCinema.Model.Collection
    {
        public class SitecoreCinemaModel
        {
            public static XdbModel Model { get; } = BuildModel();
    
            private static XdbModel BuildModel()
            {
                XdbModelBuilder modelBuilder = new XdbModelBuilder("SitecoreCinemaModel", new XdbModelVersion(1, 0));
    
                modelBuilder.DefineFacet<Contact, CinemaVisitorInfo>(FacetKeys.CinemaVisitorInfo);
                modelBuilder.DefineFacet<Interaction, CinemaInfo>(FacetKeys.CinemaInfo);
                modelBuilder.DefineEventType<WatchMovie>(false);
                modelBuilder.DefineEventType<BuyConcessions>(false);
                modelBuilder.DefineEventType<UseSelfService>(false);
                modelBuilder.ReferenceModel(Sitecore.XConnect.Collection.Model.CollectionModel.Model);
    
                return modelBuilder.BuildModel();
            }
        }
    
        public class FacetKeys
        {
            public const string CinemaInfo = "CinemaInfo";
            public const string CinemaVisitorInfo = "CinemaVisitorInfo";
        }
    }
    
  4. Save the SitecoreCinemaModel.cs file.

Create the Console.Deploy console app project

The Console.Deploy console app serializes SitecoreCinemaModel to JSON format.

To create the Console.Deploy console app project:

  1. In Visual Studio with administrator privileges, right-click the SitecoreCinema solution and click Add, New Project to create a new console app project for .NET Framework called Console.Deploy:

    The Configure your new project dialogue box in Visual Studio.
  2. In Solution Explorer, right-click Console.Deploy/References and click Add Reference.

  3. In the Reference Manager window, on the Projects tab, select SitecoreCinema.

  4. In the Reference Manager window, click Browse and add the following reference from your <xConnect instance>\bin\ folder:

    • Sitecore.XConnect.dll

  5. Click OK.

  6. In Solution Explorer, double-click the Console.Deploy/Program.cs file.

  7. In the Program.cs file, replace the content with the following code:

    using System.IO;
    
    namespace SitecoreCinema.Console.Deploy
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                System.Console.WriteLine("Generating your model ...");
                var model = SitecoreCinema.Model.Collection.SitecoreCinemaModel.Model;
                string filePath = "C:\\" + model.FullName + ".json";
                var serializedModel = Sitecore.XConnect.Serialization.XdbModelWriter.Serialize(model);
                File.WriteAllText(filePath, serializedModel);
                System.Console.WriteLine("Your model is here: " + filePath);
                System.Console.WriteLine("Press any key to continue!");
                System.Console.ReadKey();
            }
        }
    }
  8. Save the Program.cs file.

Serialize the SitecoreCinemaModel collection model

To serialize the SitecoreCinemaModel collection model:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click the Console.Deploy project and click Set as Startup Project.

  2. Click Start to run the Console.Deploy app and wait for it to serialize the model:

    A terminal with the model details in the app output.

    The serialized model in the C:\SitecoreCinemaModel, 1.0.json file must contain the following:

    {
      "Name": "SitecoreCinemaModel",
      "Version": "1.0",
      "References": [
        {
          "Name": "XConnect",
          "Version": "1.0"
        },
        {
          "Name": "Sitecore.XConnect.Collection.Model",
          "Version": "9.3"
        }
      ],
      "Types": {
        "SitecoreCinema.Model.Collection.CinemaVisitorInfo": {
          "Type": "Facet",
          "BaseType": "Sitecore.XConnect.Facet",
          "ClrType": "SitecoreCinema.Model.Collection.CinemaVisitorInfo, SitecoreCinema, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
          "Properties": {
            "FavoriteMovie": {
              "Type": "String"
            }
          }
        },
        "SitecoreCinema.Model.Collection.CinemaInfo": {
          "Type": "Facet",
          "BaseType": "Sitecore.XConnect.Facet",
          "ClrType": "SitecoreCinema.Model.Collection.CinemaInfo, SitecoreCinema, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
          "Properties": {
            "CinemaId": {
              "Type": "Int32"
            }
          }
        },
        "SitecoreCinema.Model.Collection.WatchMovie": {
          "Type": "Event",
          "BaseType": "Sitecore.XConnect.Outcome",
          "ClrType": "SitecoreCinema.Model.Collection.WatchMovie, SitecoreCinema, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
          "Properties": {
            "EIDR": {
              "Type": "String"
            }
          }
        },
        "SitecoreCinema.Model.Collection.BuyConcessions": {
          "Type": "Event",
          "BaseType": "Sitecore.XConnect.Outcome",
          "ClrType": "SitecoreCinema.Model.Collection.BuyConcessions, SitecoreCinema, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
          "Properties": {
            "BoughtAlcoholicDrink": {
              "Type": "Boolean"
            }
          }
        },
        "SitecoreCinema.Model.Collection.UseSelfService": {
          "Type": "Event",
          "BaseType": "Sitecore.XConnect.Event",
          "ClrType": "SitecoreCinema.Model.Collection.UseSelfService, SitecoreCinema, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
          "Properties": {}
        }
      },
      "Facets": [
        {
          "Target": "Contact",
          "Name": "CinemaVisitorInfo",
          "Type": "SitecoreCinema.Model.Collection.CinemaVisitorInfo"
        },
        {
          "Target": "Interaction",
          "Name": "CinemaInfo",
          "Type": "SitecoreCinema.Model.Collection.CinemaInfo"
        }
      ]
    }
  3. Copy the C:\SitecoreCinemaModel, 1.0.json file to your <xConnect instance>\App_data\Models\ folder.

Create the Console.Journey console app project

The Console.Journey console app simulates a visitors journey through a Sitecore Cinema:

  • The visitor buys a movie ticket at the ticket machine.

  • The visitor buys some candy at the concession stand.

  • The visitor scans the movie ticket to enter the movie theater.

To create the Console.Journey console app project:

  1. In Visual Studio with administrator privileges, right-click the SitecoreCinema solution and click Add, New Project to create a new console app project for .NET Framework called Console.Journey:

    The Configure your new project dialogue box in Visual Studio.
  2. In Solution Explorer, right-click Console.Journey/References and click Add Reference.

  3. In the Reference Manager window, on the Projects tab, select SitecoreCinema.

  4. In the Reference Manager window, click Browse and add the following references from your <xConnect instance>\bin\ folder:

    • Sitecore.XConnect.Client.dll

    • Sitecore.XConnect.Collection.Model.dll

    • Sitecore.XConnect.dll

    • Sitecore.Xdb.Common.Web.dll

  5. Click OK.

  6. In Solution Explorer, right-click References and click Manage NuGet Packages.

  7. In the NuGet window, click Browse and add the following references:

    • Microsoft.Extensions.Configuration.Abstractions

    • System.Interactive.Async.Providers

  8. In Solution Explorer, double-click the Console.Journey/Program.cs file.

  9. In the Program.cs file, replace the content with the following code:

    using Sitecore.XConnect;
    using Sitecore.XConnect.Client;
    using Sitecore.XConnect.Client.WebApi;
    using Sitecore.XConnect.Collection.Model;
    using Sitecore.XConnect.Schema;
    using Sitecore.Xdb.Common.Web;
    using SitecoreCinema.Model.Collection;
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    
    namespace SitecoreCinema.Console.Journey
    {
        public class Program
        {
            // From <xConnect instance>\App_Config\AppSettings.config
            const string CERTIFICATE_OPTIONS = 
                "StoreName=My;StoreLocation=LocalMachine;FindType=FindByThumbprint;FindValue=???";
    
            // From your installation
            const string XCONNECT_URL = "https://???XConnect.local";
    
            const string CONTACT_ID = "L94564543543543534";
            const int CINEMA_ID = 22;
            const string CONTACT_ID_SOURCE = "SitecoreCinema";
    
            public static string Identifier { get; set; }
    
            private static void Main(string[] args)
            {
    
                // Welcome to
                //   _____ _ __                            _______
                //  / ___/(_) /____  _________  ________  / ____(_)___  ___  ____ ___  ____ _
                //  \__ \/ / __/ _ \/ ___/ __ \/ ___/ _ \/ /   / / __ \/ _ \/ __ `__ \/ __ `/
                // ___/ / / /_/  __/ /__/ /_/ / /  /  __/ /___/ / / / /  __/ / / / / / /_/ /
                // ____/_/\__/\___/\___/\____/_/   \___/\____/_/_/ /_/\___/_/ /_/ /_/\__,_/
    
                //   ____            _     _
                //  |  _ \ ___  __ _(_)___| |_ ___ _ __
                //  | |_) / _ \/ _` | / __| __/ _ \ '__|
                //  |  _ <  __/ (_| | \__ \ ||  __/ |
                //  |_| \_\___|\__, |_|___/\__\___|_|
                //             |___/
    
                // You decide to register for the loyalty card scheme on the Sitecore Cinema website.            
                // Apparently you get free popcorn for every 10 times you swipe your loyalty card.
    
                Task.Run(async () => { await Register(); }).Wait();
    
                //   ___       _                      _   _               _  _   _
                //  |_ _|_ __ | |_ ___ _ __ __ _  ___| |_(_) ___  _ __  _| || |_/ |
                //   | || '_ \| __/ _ \ '__/ _  |/ __| __| |/ _ \| '_ \|_  ..  _| |
                //   | || | | | ||  __/ | | (_| | (__| |_| | (_) | | | |_      _| |
                //  |___|_| |_|\__\___|_|  \__,_|\___|\__|_|\___/|_| |_| |_||_| |_|
    
                // You visit a Sitecore Cinema and buy a movie ticket in the self service machine.
                // You swipe your loyalty card, and the machine sends the interaction to xConnect.
                // Because you are a loyalty card member, you do not pay at this point.
    
                Task.Run(async () => { await SelfServiceMachine(); }).Wait();
    
                //   ___       _                      _   _               _  _  ____
                //  |_ _|_ __ | |_ ___ _ __ __ _  ___| |_(_) ___  _ __  _| || ||___ \
                //   | || '_ \| __/ _ \ '__/ _  |/ __| __| |/ _ \| '_ \|_  ..  _|__) |
                //   | || | | | ||  __/ | | (_| | (__| |_| | (_) | | | |_      _/ __/
                //  |___|_| |_|\__\___|_|  \__,_|\___|\__|_|\___/|_| |_| |_||_||_____|
    
                // You buy some candy.
                // You swipe your loyalty card, and the machine sends the interaction to xConnect.
    
                Task.Run(async () => { await BuyCandy(); }).Wait();
    
                //   ___       _                      _   _               _  _  _____
                //  |_ _|_ __ | |_ ___ _ __ __ _  ___| |_(_) ___  _ __  _| || ||___ /
                //   | || '_ \| __/ _ \ '__/ _  |/ __| __| |/ _ \| '_ \|_  ..  _||_ \
                //   | || | | | ||  __/ | | (_| | (__| |_| | (_) | | | |_      _|__) |
                //  |___|_| |_|\__\___|_|  \__,_|\___|\__|_|\___/|_| |_| |_||_||____/
    
                // You scan your ticket and head into the movie theater.
                // Your loyalty card details are embedded in the barcode of your ticket. 
                // At this point, the system takes payment.
    
                Task.Run(async () => { await WatchAMovie(); }).Wait();
    
                System.Console.ForegroundColor = ConsoleColor.DarkGreen;
                System.Console.WriteLine("");
                System.Console.WriteLine("END OF PROGRAM.");
                System.Console.ReadKey();
            }
    
            private static async Task Register()
            {
                CertificateWebRequestHandlerModifierOptions options = 
                    CertificateWebRequestHandlerModifierOptions.Parse(CERTIFICATE_OPTIONS);
    
                var certificateModifier = new CertificateWebRequestHandlerModifier(options);
    
                List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>();
                var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20));
                clientModifiers.Add(timeoutClientModifier);
    
                var collectionClient = new CollectionWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var searchClient = new SearchWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var configurationClient = new ConfigurationWebApiClient(
                    new Uri(XCONNECT_URL + "/configuration"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var cfg = new XConnectClientConfiguration(
                    new XdbRuntimeModel(CollectionModel.Model, SitecoreCinemaModel.Model),
                    collectionClient,
                    searchClient,
                    configurationClient
                );
    
                try
                {
                    await cfg.InitializeAsync();
    
                    // Print "Register" if configuration is valid
                    var arr = new[]
                    {
                        @"  ____            _     _               ",
                        @"  |  _ \ ___  __ _(_)___| |_ ___ _ __   ",
                        @"  | |_) / _ \/ _` | / __| __/ _ \ '__|  ",
                        @"  |  _ <  __/ (_| | \__ \ ||  __/ |     ",
                        @"  |_| \_\___|\__, |_|___/\__\___|_|     ",
                        @"             |___/                      ",
                    };
                    System.Console.WindowWidth = 160;
                    foreach (string line in arr)
                        System.Console.WriteLine(line);
                    System.Console.WriteLine();
                }
                catch (XdbModelConflictException ce)
                {
                    System.Console.WriteLine("ERROR:" + ce.Message);
                    return;
                }
    
                // Initialize a client using the validated configuration
                using (var client = new XConnectClient(cfg))
                {
                    try
                    {
                        ContactIdentifier identifier = new ContactIdentifier(
                            CONTACT_ID_SOURCE,
                            CONTACT_ID + Guid.NewGuid(), 
                            ContactIdentifierType.Known
                        );
    
                        System.Console.WriteLine("We will generate an loyalty member ID for you and print it on a loyalty card.");
                        System.Console.WriteLine(
                            "Alright, your loyalty member ID is "
                            + identifier.Identifier
                            + ". Congratulations!"
                        );
    
                        Identifier = identifier.Identifier;
                        Contact contact = new Contact(new ContactIdentifier[] { identifier });
    
                        System.Console.WriteLine("What is your first name?");
                        var firstname = System.Console.ReadLine();
    
                        System.Console.WriteLine("What is your last name?");
                        var lastname = System.Console.ReadLine();
    
                        PersonalInformation personalInfo = new PersonalInformation()
                        {
                            FirstName = firstname,
                            LastName = lastname
                        };
    
                        System.Console.WriteLine("What is your favourite movie?");
                        var favouriteMovie = System.Console.ReadLine();
    
                        CinemaVisitorInfo visitorInfo = new CinemaVisitorInfo()
                        {
                            FavoriteMovie = favouriteMovie
                        };
    
                        client.AddContact(contact);
                        client.SetFacet<CinemaVisitorInfo>(contact, CinemaVisitorInfo.DefaultFacetKey, visitorInfo);
                        client.SetFacet<PersonalInformation>(contact, PersonalInformation.DefaultFacetKey, personalInfo);
    
                        var offlineChannel = Guid.NewGuid();
                        var registrationGoalId = Guid.NewGuid();
    
                        Interaction interaction = new Interaction(
                            contact,
                            InteractionInitiator.Brand,
                            offlineChannel,
                            String.Empty
                         );
    
                        interaction.Events.Add(new Goal(registrationGoalId, DateTime.UtcNow));
                        client.AddInteraction(interaction);
                        await client.SubmitAsync();
                        System.Console.WriteLine("Great! See you at the movies. Press any key to go to a Sitecore Cinema.");
                        System.Console.ReadKey();
                    }
                    catch (XdbExecutionException ex)
                    {
                        // Deal with exception
                    }
                }
            }
    
            private static async Task SelfServiceMachine()
            {
                CertificateWebRequestHandlerModifierOptions options = 
                    CertificateWebRequestHandlerModifierOptions.Parse(CERTIFICATE_OPTIONS);
    
                var certificateModifier = new CertificateWebRequestHandlerModifier(options);
                List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>();
                var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20));
                clientModifiers.Add(timeoutClientModifier);
    
                var collectionClient = new CollectionWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var searchClient = new SearchWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var configurationClient = new ConfigurationWebApiClient(
                    new Uri(XCONNECT_URL + "/configuration"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var cfg = new XConnectClientConfiguration(
                    new XdbRuntimeModel(CollectionModel.Model, SitecoreCinemaModel.Model),
                    collectionClient,
                    searchClient,
                    configurationClient
                );
    
                try
                {
                    await cfg.InitializeAsync();
    
                    // Print "Ticket" if configuration is valid
                    var arr = new[]
                    {
                        @" _____ _      _        _   ",
                        @"|_   _(_) ___| | _____| |_ ",
                        @"  | | | |/ __| |/ / _ \ __|",
                        @"  | | | | (__|   <  __/ |_ ",
                        @"  |_| |_|\___|_|\_\___|\__|",
                    };
                    System.Console.WindowWidth = 160;
                    foreach (string line in arr)
                        System.Console.WriteLine(line);
                    System.Console.WriteLine();
                }
                catch (XdbModelConflictException ce)
                {
                    System.Console.WriteLine("ERROR:" + ce.Message);
                    return;
                }
    
                // Initialize a client using the validated configuration
                using (var client = new XConnectClient(cfg))
                {
                    try
                    {
                        System.Console.ForegroundColor = ConsoleColor.Cyan;
                        System.Console.WriteLine("You swipe your loyalty card.");
                        System.Console.ForegroundColor = ConsoleColor.White;
    
                        var identifier = new IdentifiedContactReference(CONTACT_ID_SOURCE, Identifier);
    
                        var contact = await client.GetAsync<Contact>(
                            identifier,
                            new ContactExpandOptions(
                                PersonalInformation.DefaultFacetKey, 
                                CinemaVisitorInfo.DefaultFacetKey)
                        );
    
    
                        if (contact != null)
                        {
                            var personalInfo = contact.GetFacet<PersonalInformation>();
                            var cinemaInfo = contact.GetFacet<CinemaVisitorInfo>();
    
                            System.Console.WriteLine(
                                "Hello "
                                + personalInfo.FirstName
                                + " "
                                + personalInfo.LastName
                                + ". I see that your favorite film is "
                                + cinemaInfo.FavoriteMovie
                                + ". We might play that soon. Check our website or sign up for our newsletter."
                            );
    
                            var interaction = new Interaction(
                                contact,
                                InteractionInitiator.Contact,
                                Guid.NewGuid(), // The GUID should match a Sitecore channel item id
                                ""
                            );
    
                            client.SetFacet<CinemaInfo>(
                                interaction,
                                CinemaInfo.DefaultFacetKey,
                                new CinemaInfo() { CinemaId = CINEMA_ID }
                            );
    
                            interaction.Events.Add(new UseSelfService(DateTime.UtcNow));
                            client.AddInteraction(interaction);
                            await client.SubmitAsync();
                            System.Console.WriteLine("Here is your ticket. We will charge you when you use it, in case you have some sort of emergency between here and the theater ;-)");
                            System.Console.WriteLine("It is just one of those courtesies we offer loyalty card members! Press any key to buy some candy.");
                            System.Console.ReadKey();
                        }
                    }
                    catch (XdbExecutionException ex)
                    {
                        // Deal with exception
                    }
                }
    
            }
            private static async Task BuyCandy()
            {
                CertificateWebRequestHandlerModifierOptions options = 
                    CertificateWebRequestHandlerModifierOptions.Parse(CERTIFICATE_OPTIONS);
    
                var certificateModifier = new CertificateWebRequestHandlerModifier(options);
                List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>();
                var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20));
                clientModifiers.Add(timeoutClientModifier);
    
                var collectionClient = new CollectionWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var searchClient = new SearchWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var configurationClient = new ConfigurationWebApiClient(
                    new Uri(XCONNECT_URL + "/configuration"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var cfg = new XConnectClientConfiguration(
                    new XdbRuntimeModel(CollectionModel.Model, SitecoreCinemaModel.Model),
                    collectionClient,
                    searchClient,
                    configurationClient
                );
    
                try
                {
                    await cfg.InitializeAsync();
    
                    // Print "Candy" if configuration is valid
                    var arr = new[]
                    {
                        @"  ____                _       ",
                        @" / ___|__ _ _ __   __| |_   _ ",
                        @"| |   / _` | '_ \ / _` | | | |",
                        @"| |__| (_| | | | | (_| | |_| |",
                        @" \____\__,_|_| |_|\__,_|\__, |",
                        @"                        |___/ ",
                    };
                    System.Console.WindowWidth = 160;
                    foreach (string line in arr)
                        System.Console.WriteLine(line);
                    System.Console.WriteLine();
                }
                catch (XdbModelConflictException ce)
                {
                    System.Console.WriteLine("ERROR:" + ce.Message);
                    return;
                }
    
                // Initialize a client using the validated configuration
                using (var client = new XConnectClient(cfg))
                {
                    try
                    {
                        System.Console.ForegroundColor = ConsoleColor.Cyan;
                        System.Console.WriteLine("You swipe your loyalty card.");
                        System.Console.ForegroundColor = ConsoleColor.White;
    
                        var identifier = new IdentifiedContactReference(CONTACT_ID_SOURCE, Identifier);
    
                        var contact = await client.GetAsync<Contact>(
                            identifier,
                            new ContactExpandOptions(
                                PersonalInformation.DefaultFacetKey,
                                CinemaVisitorInfo.DefaultFacetKey
                            )
                        );
    
                        if (contact != null)
                        {
                            var personalInfo = contact.GetFacet<PersonalInformation>();
                            var cinemaInfo = contact.GetFacet<CinemaVisitorInfo>();
    
                            System.Console.WriteLine("Hello again " + personalInfo.FirstName + ".");
                            System.Console.WriteLine("Candy? You got it.");
    
                            var interaction = new Interaction(
                                contact,
                                InteractionInitiator.Contact,
                                Guid.NewGuid(), // The GUID should match a Sitecore channel item id
                                ""
                            );
    
                            client.SetFacet<CinemaInfo>(
                                interaction,
                                CinemaInfo.DefaultFacetKey,
                                new CinemaInfo() { CinemaId = CINEMA_ID }
                            );
    
                            interaction.Events.Add(new BuyConcessions(DateTime.UtcNow, "DKK", 150m));
                            client.AddInteraction(interaction);
                            await client.SubmitAsync();
                            System.Console.WriteLine("Enjoy the movie! Press any key to go to the movie theater.");
                            System.Console.ReadKey();
                        }
                    }
                    catch (XdbExecutionException ex)
                    {
                        // Deal with exception
                    }
                }
            }
    
            private static async Task WatchAMovie()
            {
                CertificateWebRequestHandlerModifierOptions options = 
                    CertificateWebRequestHandlerModifierOptions.Parse(CERTIFICATE_OPTIONS);
    
                var certificateModifier = new CertificateWebRequestHandlerModifier(options);
                List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>();
                var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20));
                clientModifiers.Add(timeoutClientModifier);
    
                var collectionClient = new CollectionWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var searchClient = new SearchWebApiClient(
                    new Uri(XCONNECT_URL + "/odata"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var configurationClient = new ConfigurationWebApiClient(
                    new Uri(XCONNECT_URL + "/configuration"),
                    clientModifiers,
                    new[] { certificateModifier }
                );
    
                var cfg = new XConnectClientConfiguration(
                    new XdbRuntimeModel(CollectionModel.Model, SitecoreCinemaModel.Model),
                    collectionClient,
                    searchClient,
                    configurationClient
                );
    
                try
                {
                    await cfg.InitializeAsync();
    
                    // Print "Movie" if configuration is valid
                    var arr = new[]
                    {
                        @" __  __            _      ",
                        @"|  \/  | _____   _(_) ___ ",
                        @"| |\/| |/ _ \ \ / / |/ _ \",
                        @"| |  | | (_) \ V /| |  __/",
                        @"|_|  |_|\___/ \_/ |_|\___|",
    
                            };
                    System.Console.WindowWidth = 160;
                    foreach (string line in arr)
                        System.Console.WriteLine(line);
                    System.Console.WriteLine();
                }
                catch (XdbModelConflictException ce)
                {
                    System.Console.WriteLine("ERROR:" + ce.Message);
                    return;
                }
    
                // Initialize a client using the validated configuration
                using (var client = new XConnectClient(cfg))
                {
                    try
                    {
                        System.Console.ForegroundColor = ConsoleColor.Cyan;
                        System.Console.WriteLine("You scan your ticket which has your loyalty member information embedded in its bar code.");
                        System.Console.ForegroundColor = ConsoleColor.White;
    
                        var identifier = new IdentifiedContactReference(CONTACT_ID_SOURCE, Identifier);
    
                        var contact = await client.GetAsync<Contact>(
                            identifier,
                            new ContactExpandOptions(
                                PersonalInformation.DefaultFacetKey,
                                CinemaVisitorInfo.DefaultFacetKey
                            )
                        );
    
                        if (contact != null)
                        {
                            var personalInfo = contact.GetFacet<PersonalInformation>();
                            var cinemaInfo = contact.GetFacet<CinemaVisitorInfo>();
    
                            System.Console.WriteLine("Enjoy your movie, " + personalInfo.FirstName + "!");
                            System.Console.WriteLine("Since you are a loyalty member, we will take payment for your ticket now.");
    
                            var interaction = new Interaction(
                                contact,
                                InteractionInitiator.Contact,
                                Guid.NewGuid(), // The GUID should match a Sitecore channel item id
                                ""
                            );
    
                            interaction.Events.Add(
                                new WatchMovie(DateTime.UtcNow, "DKK", 100m) { EIDR = "10.5240/08B0-F8C2-5525-BF22-BA07-4" }
                            );
    
                            client.SetFacet<CinemaInfo>(
                                interaction,
                                CinemaInfo.DefaultFacetKey,
                                new CinemaInfo() { CinemaId = CINEMA_ID }
                            );
    
                            client.AddInteraction(interaction);
                            await client.SubmitAsync();
                            System.Console.WriteLine("Before you go! Do you want to see the data we have collected about you today? :)");
    
                            var contactAgain = await client.GetAsync<Contact>(
                                identifier,
                                new ContactExpandOptions(
                                    PersonalInformation.DefaultFacetKey,
                                    CinemaVisitorInfo.DefaultFacetKey
                                )
                                {
                                    Interactions = new RelatedInteractionsExpandOptions( 
                                        new string[] { CinemaInfo.DefaultFacetKey }
                                    )
                                    {
                                        StartDateTime = DateTime.Today
                                    }
                                }
                            );
    
                            if (contactAgain != null)
                            {
                                var details = contactAgain.GetFacet<PersonalInformation>();
                                var movie = contactAgain.GetFacet<CinemaVisitorInfo>();
    
                                System.Console.WriteLine(String.Format(
                                    "Your name is {0} {1}, and your favorite movie is {2}.",
                                    details.FirstName,
                                    details.LastName,
                                    movie.FavoriteMovie
                                ));
    
                                System.Console.WriteLine(
                                    "Today you have had "
                                    + contactAgain.Interactions.Count
                                    + " interactions with us."
                                );
    
                                var i = 0;
    
                                foreach (var interactionsToday in contactAgain.Interactions)
                                {
                                    i++;
                                    var cinemaId = interactionsToday.GetFacet<CinemaInfo>(CinemaInfo.DefaultFacetKey);
                                    System.Console.WriteLine("");
    
                                    System.Console.WriteLine(
                                        "Interaction #"
                                        + i
                                        + (cinemaId != null ? " at Cinema #" + cinemaId.CinemaId : String.Empty)
                                    );
    
                                    System.Console.Write("Events: ");
    
                                    foreach (var evv in interactionsToday.Events) { 
                                        System.Console.Write(evv.GetType().ToString() + "");
                                    };
    
                                    System.Console.WriteLine("");
                                }
                            }
    
                            System.Console.ReadKey();
                        }
                    }
                    catch (XdbExecutionException ex)
                    {
                        // Deal with exception
                    }
                }
            }
        }
    }
    
  10. Edit the CERTIFICATE_THUMBPRINT and the XCONNECT_URL constants.

  11. Save the Program.cs file.

Simulate a Sitecore Cinema visit

To simulate a Sitecore Cinema visit:

  1. In Visual Studio with administrator privileges, in Solution Explorer, right-click the Console.Journey project and click Set as Startup Project.

  2. Click Start to run the Console.Journey app and wait for it to:

    • Register your first name.

    • Register your second name.

    • Register your favorite movie.

    • Simulate buying a movie ticket.

    • Simulate buying candy.

    • Simulate entering the movie theater.

    • Show the collected contact information and interactions.

    A terminal with the membership registration details in the app output.