Use xConnect to update Contact Facets and Identifier in xDB – Sitecore Commerce (XC) 9 Engine

In this video I am going to walk you through how to use xConnect in a Sitecore Commerce Engine solution.

We will:

* Add / Update Loyalty Points Facet
* Add / Update Commerce Customer ID Facet
* Add an Identifier to an existing Contact
* Use xConnect in a Sitecore Commerce 9 Engine project

Useful links:

* Getting Started with xConnect
* Creating a Contact Facet in xDB Sitecore 9 & xConnect Magic!
* GitHub Repo: https://github.com/konabos/xConnectLoyalty

If you have any questions or concerns, please get in touch with me. (@akshaysura13 on twitter or on Slack).

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Threading.Tasks;
    5. using Konabos.XConnect.Loyalty.Model.Facets;
    6. using Konabos.XConnect.Loyalty.Model.Models;
    7. using Sitecore.Commerce.Core;
    8. using Sitecore.Commerce.Plugin.Customers;
    9. using Sitecore.XConnect;
    10. using Sitecore.XConnect.Client;
    11. using Sitecore.XConnect.Client.WebApi;
    12. using Sitecore.XConnect.Collection.Model;
    13. using Sitecore.XConnect.Schema;
    14. using Sitecore.Xdb.Common.Web;
    15. using Plugin.Konabos.Loyalty.Components;
    16. using Sitecore.Framework.Pipelines;
    17. using Microsoft.Extensions.Logging;
    18.  
    19. namespace Konabos.Loyalty.Feature.ReconcilePoints.Pipelines.Blocks
    20. {
    21. public class UpdateCustomerInxDBBlock : PipelineBlock<Customer, Customer, CommercePipelineExecutionContext>
    22. {
    23. private readonly IPersistEntityPipeline _persistEntityPipeline;
    24. private readonly IFindEntitiesInListPipeline _findEntitiesInListPipeline;
    25.  
    26. public UpdateCustomerInxDBBlock(IPersistEntityPipeline persistEntityPipeline, IFindEntitiesInListPipeline findEntitiesInListPipeline)
    27. {
    28. _persistEntityPipeline = persistEntityPipeline;
    29. _findEntitiesInListPipeline = findEntitiesInListPipeline;
    30. }
    31.  
    32. public override async Task<Customer> Run(Customer customer, CommercePipelineExecutionContext context)
    33. {
    34. CertificateWebRequestHandlerModifierOptions options =
    35. CertificateWebRequestHandlerModifierOptions.Parse("StoreName=My;StoreLocation=LocalMachine;FindType=FindByThumbprint;FindValue=BC9B7186102910E8F34EE8D9F38138203F7555BA");
    36.  
    37. var certificateModifier = new CertificateWebRequestHandlerModifier(options);
    38.  
    39. List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>();
    40. var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20));
    41. clientModifiers.Add(timeoutClientModifier);
    42.  
    43. var collectionClient = new CollectionWebApiClient(new Uri("https://cateringdemo.xconnect.dev.local/odata"), clientModifiers, new[] { certificateModifier });
    44. var searchClient = new SearchWebApiClient(new Uri("https://cateringdemo.xconnect.dev.local/odata"), clientModifiers, new[] { certificateModifier });
    45. var configurationClient = new ConfigurationWebApiClient(new Uri("https://cateringdemo.xconnect.dev.local/configuration"), clientModifiers, new[] { certificateModifier });
    46.  
    47. var cfg = new XConnectClientConfiguration(
    48. new XdbRuntimeModel(LoyaltyModel.Model), collectionClient, searchClient, configurationClient);
    49.  
    50. try
    51. {
    52. await cfg.InitializeAsync();
    53. }
    54. catch (XdbModelConflictException ce)
    55. {
    56. context.Logger.LogError(string.Format("{0}-Error in UpdateCustomerInxDBBlock connecting to xDB : {1}", (object)this.Name, (object)ce.Message), Array.Empty<object>());
    57. }
    58. using (var client = new XConnectClient(cfg))
    59. {
    60. try
    61. {
    62. IdentifiedContactReference reference = new IdentifiedContactReference("CommerceUser", string.Concat(customer.Domain, "\", customer.Email));
    63. Contact existingContact = client.Get<Contact>(reference, new ContactExpandOptions(new string[] { PersonalInformation.DefaultFacetKey, LoyaltyPointsFacet.DefaultFacetKey, LoyaltyCommerceFacet.DefaultFacetKey }));
    64.  
    65. if (existingContact != null)
    66. {
    67. //Add an identifier for the contact with the Commerce Customer Id
    68. string identifierSource = "LoyaltyCustomerId";
    69. var loyaltyCustomerIdentifier = existingContact.Identifiers.Where(i => i.Source == identifierSource).FirstOrDefault();
    70. if (loyaltyCustomerIdentifier == null)
    71. {
    72. client.AddContactIdentifier(existingContact, new ContactIdentifier(identifierSource, customer.Id.ToString(), ContactIdentifierType.Known));
    73. client.Submit();
    74. }
    75.  
    76. //Add or Update Loyalty Points for the customer
    77. LoyaltyPointsFacet loyaltyPointFacet = existingContact.GetFacet<LoyaltyPointsFacet>(LoyaltyPointsFacet.DefaultFacetKey);
    78. if (loyaltyPointFacet == null)
    79. loyaltyPointFacet = new LoyaltyPointsFacet();
    80.  
    81. loyaltyPointFacet.PointsEarned = customer.GetComponent<LoyaltyComponent>().PointsEarned;
    82. loyaltyPointFacet.PointsSpent = customer.GetComponent<LoyaltyComponent>().PointsSpent;
    83.  
    84. client.SetFacet<LoyaltyPointsFacet>(existingContact, LoyaltyPointsFacet.DefaultFacetKey, loyaltyPointFacet);
    85. client.Submit();
    86.  
    87. //Add or Update the Commerce Customer ID
    88. LoyaltyCommerceFacet loyaltyCommerceFacet = existingContact.GetFacet<LoyaltyCommerceFacet>(LoyaltyCommerceFacet.DefaultFacetKey);
    89. if (loyaltyCommerceFacet == null)
    90. loyaltyCommerceFacet = new LoyaltyCommerceFacet();
    91.  
    92. loyaltyCommerceFacet.CommerceCustomerId = customer.Id.ToString();
    93. client.SetFacet<LoyaltyCommerceFacet>(existingContact, LoyaltyCommerceFacet.DefaultFacetKey, loyaltyCommerceFacet);
    94. client.Submit();
    95. }
    96. }
    97. catch (XdbExecutionException ex)
    98. {
    99. context.Logger.LogError(string.Format("{0}-Error in UpdateCustomerInxDBBlock updating customer {2} to xDB : {1}", (object)this.Name, (object)ex.Message, customer.Id.ToString()), Array.Empty<object>());
    100. }
    101. }
    102. return customer;
    103. }
    104. }
    105. }

You might get the following error while the running xConnect code:

    1. [Error] ["XdbContextLoggingPlugin"] XdbContext Batch Execution Exception
    2. System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
    3. at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)
    4. at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
    5. --- End of inner exception stack trace ---
    6. at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
    7. at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
    8. --- End of inner exception stack trace ---
    9. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    10. at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    11. at Sitecore.Xdb.Collection.Search.Solr.SolrReader.<ExecuteQuery>d__14`1.MoveNext()
    12. --- End of stack trace from previous location where exception was thrown ---
    13. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    14. at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    15. at Sitecore.Xdb.Collection.Search.Solr.SolrReader.<GetSearchResults>d__13`2.MoveNext()
    16. --- End of stack trace from previous location where exception was thrown ---
    17. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    18. at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    19. at Sitecore.Xdb.Collection.Search.Solr.SolrReader.<SearchContacts>d__10.MoveNext()
    20. --- End of stack trace from previous location where exception was thrown ---
    21. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    22. at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    23. at Sitecore.Xdb.Collection.Repository.<SearchContacts>d__11.MoveNext()
    24. --- End of stack trace from previous location where exception was thrown ---
    25. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    26. at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    27. at Sitecore.Xdb.Collection.RepositoryCountersDecorator.<SearchContacts>d__8.MoveNext()
    28. --- End of stack trace from previous location where exception was thrown ---
    29. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    30. at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    31. at Sitecore.XConnect.Service.RepositorySearchInvoker.<Execute>d__7.MoveNext()

Please add your Solr cert to the Local Computer\Trusted Root Certification Authorities.

You might get the following error while the running xConnect code:

    1. 2018-08-25 15:01:10.453 -07:00 [Information] XConnect Test Host Application Start, Machine: "DESKTOP-BCOM790", Site: "cateringdemo.xconnect.dev.local", AppId: "/LM/W3SVC/2/ROOT"
    2. 2018-08-25 15:01:11.584 -07:00 [Information] Certificate Validation Filter Enabled, Thumbprint: BC9B7186102910E8F34EE8D9F38138203F7555BA
    3. 2018-08-25 15:01:11.585 -07:00 [Information] SSL Validation Filter Enabled
    4. 2018-08-25 15:01:12.865 -07:00 [Information] SystemPerformanceCounters Constructor, Instance:cateringdemo.xconnect.dev.local, Path: C:inetpubwwwrootcateringdemo.xconnect.dev.localApp_DataDiagnostics, CounterFilePattern: *.json
    5. 2018-08-25 15:01:12.882 -07:00 [Error] Access to the registry key 'Global' is denied.
    6. System.UnauthorizedAccessException: Access to the registry key 'Global' is denied.
    7. at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
    8. at Microsoft.Win32.RegistryKey.InternalGetValue(String name, Object defaultValue, Boolean doNotExpand, Boolean checkSecurity)
    9. at Microsoft.Win32.RegistryKey.GetValue(String name)
    10. at System.Diagnostics.PerformanceMonitor.GetData(String item)
    11. at System.Diagnostics.PerformanceCounterLib.GetPerformanceData(String item)
    12. at System.Diagnostics.PerformanceCounterLib.get_CategoryTable()
    13. at System.Diagnostics.PerformanceCounterLib.CategoryExists(String machine, String category)
    14. at System.Diagnostics.PerformanceCounterCategory.Exists(String categoryName, String machineName)
    15. at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
    16. at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
    17. at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
    18. at Sitecore.XConnect.Diagnostics.PerformanceCounters.SystemPerformanceCounters.Initialize(String counterFilePattern)
    19. 2018-08-25 15:01:13.331 -07:00 [Information] Create "XdbContextLoggingPlugin"
    20. 2018-08-25 15:01:13.341 -07:00 [Information] Register "XdbContextLoggingPlugin"

Please add your xConnect site app pool user to the Performance Monitor Users group.