codeflood logo

The Reference Data Service

Sitecore 9.0 introduces the evolution of xDB, xConnect. xConnect is a complete (almost) rebuild of the customer behavior collection and reporting capabilities of Sitecore (plus more). These capabilities are now delivered through a new set of separate applications. In this post, I'll be introducing you to the Reference Data Service, which is a completely new capability that was introduced with xConnect.

What is it?

The Reference Data Service is a repository of definition data. Definitions can be anything that is required to be referenced within the xConnect environment. To properly understand what the Reference Data service is, we need to understand the problem it solves.

In previous versions of Sitecore, all the xDB capabilities had access to the content tree where all the marketing definitions resided, as these capabilities were developed inside a Sitecore CMS instance. The marketing definitions (such as goal definitions, page event definitions and outcome definitions as well as others) are required so the processing role can lookup the definitions by ID. When we developed xConnect, we wanted to create completely new applications that weren't based on the standard Sitecore CMS, as it contains much functionality that isn't required for these new applications. For example, we don't need the rendering pipelines, or all the normal Sitecore UI.

Because we wouldn't have the Sitecore kernel available, we wouldn't have a content tree where all the marketing definitions are stored. Instead, we needed a way to store those definitions within the xConnect environment. And that is the problem which the Reference Data Service solves. It allows us to store the marketing definitions and access them within xConnect.

Use it yourself

You can also store your own definitions inside the Reference Data Service, so you can reference your own domain objects in the same way Sitecore does the xConnect collection model. To illustrate how to do this, let's define a scenario.

Let's say we're working for a company that sells guided tours. When we want to trigger an xConnect event, such as a goal or an outcome, we want to be able to reference the tour which the event is relevant for, without having to replicate all the tour data into the event object. Instead, we'd like to reference the tour by some ID instead, then when we need additional tour data during processing, we can lookup the tour using that ID and one of the Reference Data clients.

Define our Definition

To store our own definitions, we need to create the classes which will hold the definition data. Definitions are localizable, so in additional to defining the common data which is shared across all cultures, we also need to define the culture specific data. Both of these classes are just POCOs (Plain Old C# Objects). So let's start with defining the common and cultural classes for the tour definition.

public class TourCommon
{
	public string CountryCode { get; set; }
	public Guid Operator { get; set; }
	public TimeSpan Duration { get; set; }
}

public class TourCulture
{
	public string Name { get; set; }
	public string Description { get; set; }
}

You might notice I've not included an ID in the TourCommon class. This is because we'll use the ID as part of the DefinitionKey, which is used to identify a single definition.

Accessing the Reference Data Client

As with the rest of xConnect, the Reference Data client can be used from any .net application, not just from inside of a Sitecore CMS instance. There are actually 2 Reference Data clients, a read-only client and a read-write client. You can use whichever is appropriate for your context.

From inside Sitecore

Both clients are available in the Sitecore IoC container, so it's available through dependency injection (depending on the class in question), or by accessing the IoC container directly:

using Microsoft.Extensions.DependencyInjection;
using Sitecore.DependencyInjection;
using Sitecore.Xdb.ReferenceData.Core;

var readonlyClient = ServiceLocator.ServiceProvider.GetRequiredService<IReadOnlyReferenceDataClient>();
var readWriteClient = ServiceLocator.ServiceProvider.GetRequiredService<IReferenceDataClient>();

From inside xConnect

The clients are also available inside many of the xConnect roles. They're present in the IoC container and various services also expose them in other places.

For example, if you are creating a marketing automation activity the read-write reference data client is available from the ActivityServices property under a property named Reference.

Identifying a Definition

Now we have access to the client, we can identify a definition we want to store.

The DefinitionKey class is used to identify a single definition. It consists of 3 parts:

  • A definition type
  • A definition moniker
  • a version number

The definition type is a name used to categorise definitions. This is done to allow reuse of a moniker across different types, kind of like a namespace. It's also used to retrieve all the definitions with the same type. Before we can create a definition key, we must get a definition type key. Both the definition type key and the definition moniker can be considered text values, which are case-sensitive.

Get the Definition Type Key

There are 2 different ways to get a definition type key, depending on whether you're saving definitions or retrieving definitions.

If saving definitions, you'll be using the read-write client, and you'll want to use the EnsureDefinitionKey method. This method will retrieve the key if it exists, or create it and return it if it doesn't.

If you're only retrieving definitions, and don't want to accidentally create a definition type if it doesn't already exist, then use the GetDefinitionKey method which is available on both clients.

var definitionTypeKey = await client.EnsureDefinitionTypeAsync("Tour");
var existingDefinitionTypeKey = await readOnlyClient.GetDefinitionTypeAsync("Tour");

Keep in mind, the definition type key is case-sensitive.

Create the Definition Key

Once we have the DefinitionTypeKey, we can instantiate the DefinitionKey used to identify the definition.

var definitionKey = new DefinitionKey("the-tour", definitionTypeKey, 4);

The last parameter is the version number. That's right, definitions are versionable, so to identify a single definition, the version number must also be provided.

Saving a Definition

Saving a definition is pretty straight forward. Simply instantiate the definition using the definition key, then call the Save method.

var definition = new Definition<TourCommon, TourCulture>(definitionKey)
{
	CommonData = new TourCommon
	{
		CountryCode = "AU",
		Operator = operatorId,
		Duration = TimeSpan.FromMinutes(90)
	},
	CultureData =
	{
		{	
			new CultureInfo("en"),
			new TourCulture
			{
				Name = "Some Tour",
				Description = "Lorem ipsum dolor sit amed"
			}
		},
		{	
			new CultureInfo("da"),
			new TourCulture
			{
				Name = "Some Tour",
				Description = "Lorem ipsum dolor sit amed"
			}
		}
	},
	IsActive = true
};

var saveResult = await client.SaveAsync(definition);

When saving the definition, if the version (given by the version number) already exists, the version will be overwritten. If the version does not exist, it will be added.

Note how the culture data was defined above. The CultureData property of the Definition<TCommon, TCulture> class is a dictionary which uses a CultureInfo class as the key and an instance of the TourCulture class as the value.

Retrieving a Definition

To retrieve a definition we need to identify it just like when saving the definition, however in this case, the version is optional. Often you will want to retrieve the latest active version of the definition, rather than specify the exact version. And you may not know the current latest version ahead of time.

To specify the definition you want to retrieve, use an instance of DefinitionCriteria. It still uses the definition type key like above, and the moniker, but the version is optional.

var criteria = new DefinitionCriteria("the-tour", definitionTypeKey);

Keep in mind the definition moniker is case-sensitive.

The cultural data returned is determined by the Culture property of the definition criteria. If this is set to null, then all cultural data available for that version will be returned. If you only want the cultural data for a single culture, provide that culture in the Culture property.

var criteria = new DefinitionCriteria("the-tour", definitionTypeKey);
criteria.Culture = new CultureInfo("da");

Now we can retrieve the definition.

var definition = await client.GetDefinitionAsync<TourCommon, TourCulture>(criteria, true);

The second boolean parameter determines whether to get the latest active version of the definition.

With the definition in hand, we can now access the POCOs we defined above through the CommonData property for the class holding the data common for all cultures, and the CultureData dictionary for culture specific data.

var tourCommonData = definition.CommonData;
var tourDanishData = definition.CultureData[new CultureInfo("da")];

Conclusion

The new Reference Data Service is not particularly complex, but this post is only scratching the surface. Nonetheless, with this information, you can now make use of the Reference Data Service to store your own domain objects to be used as lookups inside of xConnect. For more details information, check out the official documentation at https://doc.sitecore.net/developers/xp/reference-data-service/introduction/.

Comments

Leave a comment

All fields are required.