ListManager – Duplicate contacts with the same email address

Most of you will already know, but all Contact Identifiers within XConnect are case-sensitive. This means that your-email@test.com is different than your-email@TEST.COM.

One should expect that the List Manager converts all emailaddresses to lowercase, so the import would be consistent, and that it does not depend on the Markteer knowing that he should have converted all the emailaddresses into a consistent format.

This is where the problem arises; the Marketeer is not aware (and should not have to be) that the identifiers are case-sensitive. When a Marketeer creates a Contact List by uploading a CSV file, all data is imported as-is, meaning that the casing of the email address stays as is as well. This leads to problems where users unsubscribe from lists where they have been added twice (because the user has multiple contacts) and they still recieve email. A blog post on how to resolve all these duplicates will be posted later.

The solution

We can hook into the processing logic of the List Manager Import to transform the identifier to lowercase, by adding a config file and a custom IImportContactSource. In the configuration example below change YourProject to the proper namespace and don’t forget to add the NuGet package Sitecore.ListManagement

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <import>
      <contactSource type="Sitecore.ListManagement.Import.ValidIdentifierSourceFilter, Sitecore.ListManagement" patch:instead="contactSource">
        <param type="Sitecore.ListManagement.Import.UniqueIdentifierFilter, Sitecore.ListManagement">
          <param type="YourProject.LowerCaseIdentifier, YourProject">
            <param type="Sitecore.ListManagement.Import.RequiredFieldsFilter, Sitecore.ListManagement">
              <param type="Sitecore.ListManagement.Import.IdentifierIndexRangeFilter, Sitecore.ListManagement">
                <param type="Sitecore.ListManagement.Import.CsvContactSource, Sitecore.ListManagement">
                  <param type="Sitecore.ListManagement.Import.CsvReader, Sitecore.ListManagement" resolve="true"/>
                </param>
              </param>
            </param>
          </param>
        </param>
        <param resolve="true" type="Sitecore.Abstractions.BaseLog, Sitecore.Kernel"/>
      </contactSource>
    </import>
  </sitecore>
</configuration>
using System.Collections.Generic;
using System.IO;
using Sitecore.Diagnostics;
using Sitecore.ListManagement.Import;

namespace YourProject
{
    public class LowerCaseIdentifier : IImportContactSource
    {
        private readonly IImportContactSource _source;

        public LowerCaseIdentifier(
            IImportContactSource source)
        {
            Assert.ArgumentNotNull(source, nameof(source));
            _source = source;
        }

        public IEnumerable<string[]> Read(
            Stream stream,
            ImportOptions options,
            ImportSummary summary)
        {
            Assert.ArgumentNotNull(stream, nameof(stream));
            Assert.ArgumentNotNull(options, nameof(options));
            Assert.ArgumentNotNull(summary, nameof(summary));

            var mappings = options.Mappings;
            var identifierIndex = mappings.IdentifierIndex;
            var result = _source.Read(stream, options, summary);
            foreach (var row in result)
            {
                row[identifierIndex] = row[identifierIndex].ToLowerInvariant();
                yield return row;
            }
        }
    }
}

Leave a Reply

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