Sitecore XConnect – Merging marketing preferences

When a contact is merged with another contact, only facets that do not exist on the target contact will be copied. This means that when both the Source and Target contact have a value set in their Marketing Preferences facet, only the Marketing Prefences from the Target contact will be kept. This can cause unwanted side effects, because it could be that the Source contact unsubscribed from a Marketing Preference, while the Target contact has not.

Merge Handlers

Within Sitecore a facet can have a Merge Handler configured, which will be executed when a contact is merged. Within this handler you will have access to both the source facet as well as the target facet, and you can compute the new facet value. The Marketing Preferences are stored in the ExmKeyBehaviorCache facet, and while this facet has a merge handler, only the Sending Limits and the Email Events are merged.

To make sure the Marketing Preferences are merged as well, we have to override the default ExmKeyBehaviorCacheMergeHandler and to extend it so it also merges the Marketing Preferences. This process happens on the XConnect instance, so it should be deployed there. Please see the sample code below.

Sample code


using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Sitecore.EmailCampaign.Core.Services;
using Sitecore.EmailCampaign.Model.XConnect.Facets;
using Sitecore.EmailCampaign.XConnect.Handlers;

namespace Example.EmailCampaign.MergeHandler
    public class CustomExmKeyBehaviorCacheHandler : ExmKeyBehaviorCacheHandler
        public CustomExmKeyBehaviorCacheHandler(ILogger<ExmKeyBehaviorCacheHandler> logger, IEmailPeriodService emailPeriodService) : base(logger, emailPeriodService)

        protected override bool Merge(ExmKeyBehaviorCache source, ExmKeyBehaviorCache target)
            var baseResult = base.Merge(source, target);
            return baseResult | MergeMarketingPreferences(source, target);

        public bool MergeMarketingPreferences(ExmKeyBehaviorCache source, ExmKeyBehaviorCache target)
            if (source?.MarketingPreferences == null || target == null)
                return false;

            if (target.MarketingPreferences == null)
                target.MarketingPreferences = new List<MarketingPreference>();
            bool anythingChanged = false;

            foreach (var sourceMarketingPreference in source.MarketingPreferences)
                var targetMarketingPreference = target.MarketingPreferences
                    .Where(targetPreference => targetPreference.ManagerRootId == sourceMarketingPreference.ManagerRootId)
                    .Where(targetPreference => targetPreference.MarketingCategoryId == sourceMarketingPreference.MarketingCategoryId)

                if (targetMarketingPreference == null)
                    anythingChanged = true;
                    if (sourceMarketingPreference.DateTime > targetMarketingPreference.DateTime)
                        targetMarketingPreference.Preference = sourceMarketingPreference.Preference;
                        anythingChanged = true;

            return anythingChanged;


<?xml version="1.0" encoding="utf-8" ?>
                        <Type>Example.EmailCampaign.MergeHandler.CustomExmKeyBehaviorCacheHandler, Example.EmailCampaign.MergeHandler</Type>
                        <As>Sitecore.XConnect.Service.IContactMergeHandler, Sitecore.XConnect.Service</As>

You should add the following NuGet packages to compile the sample above:

  • Sitecore.EmailCampaign.Core
  • Sitecore.EmailCampaign.Model
  • Sitecore.EmailCampaign.XConnect


The dll file should be deployed to the bin folder of your XConnect instance, and the xml file should be deployed to the /App_Data/Config/Sitecore/Z_Custom/ folder of your XConnect instance.

Leave a Reply

Your email address will not be published. Required fields are marked *