I was working on a project that had a moderate level of XConnect integration and custom contact facets. As I was learning and implementing the XConnect API I quickly found my code growing unruly and hard to follow.
The official Sitecore documentation is a great reference for learning specific calls. It is useful to read through and gain familiarity with the system. However as I attempted to handle real world scenarios I needed to combine several documented examples. I needed to handle things like:
• If contact is new, first commit it to XConnect
• If editing a contact facet that does not yet exist, instantiate the facet class to avoid null error
• Support partial updates, that is if facet data already exists, load data first so setting one property does not overwrite the others with empty values.
• After saving, refresh Web Tracker**
• Shared computer issues - If editing a contact that is not known to be the same as current (cookied) contact, create new contact in XConnect instead of using current.
Things of this sort quickly piled up a plate of spaghetti code. It was difficult to read, ugly to maintain, and prone to copy and paste mistakes. This led me to create a reusable utility with these goals:
• Consistency - Handle all the leg work of XConnect API in a repeatable way (reusable methods)
• Agility - Address various states of data (use cases above)
• Readability - Focus on intention of edit (least amount of inputs possible)
The current solve is one custom helper method:
It boils down all common needs to the least amount of information possible.
• The SetContactFacet<> method uses C# generics so any facet can be specified, whether built-in or custom.
• Using generics also enables intelli-sense for the doUpdates input. The doUpdates parameter is functional notation supporting partial updates (just set what you want and don't worry about the other properties you aren't setting).
• contactIdentifier specifies the who
• facetKey is an unfortunate necessity, but needed nonetheless. It is usually [FacetType].DefaultFacetKey (but cannot assume all custom facets follow that pattern).
When a Facet does not yet exist for a contact, then its <Type> is instantiated with a parameter-less constructor call.
Some facet classes require constructor parameters. For this need, there is an optional createNew parameter:
CreateNew is only called if facet does not yet exist. Whether the Facet is new or old, DoUpdates is always executed.
While this helper is perfect for changes on a single facet type, it is not ideal for editing across multiple facets. Each call needs to load the contact and facet data from XConnect, submit it back, and likely purge the session data to be refreshed for the Web Tracker. It would be better if we wrap all these edits and consolidate communication.
For this simply wrap multiple calls with BeginEdit() and EndEdit():
Initially I wanted to wrap it with a using statement, but decided to avoid that for the same reasons discussed about Sitecore’s item edit context. If there is an exception inside the block we don’t want to commit any edits. Avoiding the using block achieves this.
Here is an example of grouping multiple facets:
In the end, I was happy with the cleanliness, and consistency aiding testing and reusability.
An implementation of this helper is available here in two files:
XConnectService – implements logical flow - single versus multiple
XConnectServiceOperations – implements the nuts and bolts
**What is Web Tracker? A tip for those new to Sitecore 9: XConnect is now a standalone we app with RESTful api. Sitecore is now a client of that XConnect app, and therefore needs to use XConnect Client API. Sitecore ships with this client api already implemented referenced as Web Tracker. It covers all the same create visit, personalization rules, and end session save to xDB stuff that Sitecore 8 had. However, it is important to know that the session data loaded by Web Tracker for use in the Sitecore application is now read-only. If you want to write data to contact facets you must write directly to XConnect using the XConnect Client API. If you do that, the Web Tracker will be unaware, so you then need to flush the Web Tracker session data so it can reload with fresh data from XConnect. The helper above handles this need as well.
Happy trekking and tracking!