Maybe it is not the most elegant solution, but you can try.
First of all you need to create a new pipeline step template which is inherited from Resolve Contact by Identifier from xConnect Pipeline Step
template. Let's call it Extended Resolve Contact by Identifier from xConnect Pipeline Step
. Add three extra fields to the identifier
section:
FirstNameValueAccessor
type: Droptree; source: query:./ancestor-or-self::[@@templateid='{327A381B-59F8-4E88-B331-BEBC7BD87E4E}']//descendant-or-self::[@@templateid='{ADD2D00A-784B-492F-94CB-971303DF4019}'])
LastNameValueAccessor
type: Droptree; source: query:./ancestor-or-self::[@@templateid='{327A381B-59F8-4E88-B331-BEBC7BD87E4E}']//descendant-or-self::[@@templateid='{ADD2D00A-784B-492F-94CB-971303DF4019}'])
BirthDateValueAccessor
type: Droptree; source: query:./ancestor-or-self::[@@templateid='{327A381B-59F8-4E88-B331-BEBC7BD87E4E}']//descendant-or-self::[@@templateid='{ADD2D00A-784B-492F-94CB-971303DF4019}'])
Then, we need to create a new CustomResolveXConnectContactStepConverter
:
using Sitecore.DataExchange.Attributes;
using Sitecore.DataExchange.DataAccess;
using Sitecore.DataExchange.Models;
using Sitecore.DataExchange.Providers.XConnect.Converters.PipelineSteps;
using Sitecore.DataExchange.Repositories;
using Sitecore.Services.Core.Model;
namespace PipelineBatchRunner
{
[SupportedIds(new string[] { "{TEMPLATE IF OF CustomResolveXConnectContactStepPROCESSOR }" })]
public class CustomResolveXConnectContactStepConverter : ResolveXConnectContactByIdentifierStepConverter
{
public const string FirstNameValueAccessor = "FirstNameValueAccessor";
public const string LastNameValueAccessor = "LastNameValueAccessor";
public const string BirthDateValueAccessor = "BirthDateValueAccessor";
public CustomResolveXConnectContactStepConverter(IItemModelRepository repository) : base(repository)
{
}
protected override void AddPlugins(ItemModel source, PipelineStep pipelineStep)
{
base.AddPlugins(source, pipelineStep);
ContactFallbackResolverSettings fallbackPlugin = new ContactFallbackResolverSettings()
{
FirstNameValueAccessor = this.ConvertReferenceToModel<IValueAccessor>(source, FirstNameValueAccessor),
LastNameValueAccessor = this.ConvertReferenceToModel<IValueAccessor>(source, LastNameValueAccessor),
BirthDateValueAccessor = this.ConvertReferenceToModel<IValueAccessor>(source, BirthDateValueAccessor),
};
pipelineStep.AddPlugin<ContactFallbackResolverSettings>(fallbackPlugin);
}
}
}
and CustomResolveXConnectContactStepProcessor
:
using System;
using System.Linq;
using Sitecore.DataExchange.Attributes;
using Sitecore.DataExchange.Contexts;
using Sitecore.DataExchange.DataAccess;
using Sitecore.DataExchange.Models;
using Sitecore.DataExchange.Plugins;
using Sitecore.DataExchange.Providers.XConnect;
using Sitecore.DataExchange.Providers.XConnect.Plugins;
using Sitecore.DataExchange.Providers.XConnect.Processors.PipelineSteps;
using Sitecore.Services.Core.Diagnostics;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;
using Sitecore.XConnect.Collection.Model;
namespace PipelineBatchRunner
{
[RequiredPipelineStepPlugins(new Type[] { typeof(XConnectContactIdentifierSettings), typeof(LoadInteractionsSettings), typeof(RetrySettings), typeof(ContactFallbackResolverSettings) })]
public class CustomResolveXConnectContactStepProcessor : ResolveXConnectContactByIdentifierStepProcessor
{
protected override object FindExistingObject(Sitecore.XConnect.IXdbContext client, IXConnectClientHelper clientHelper,
string identifierValue, PipelineStep pipelineStep, PipelineContext pipelineContext, ILogger logger)
{
var contact = base.FindExistingObject(client, clientHelper, identifierValue, pipelineStep, pipelineContext, logger);
if (contact == null)
{
ContactFallbackResolverSettings plugin = pipelineStep.GetPlugin<ContactFallbackResolverSettings>();
if (plugin == null)
return null;
ContactExpandOptions contactExpandOptions = GetContactExpandOptions(pipelineStep, pipelineContext, logger);
Contact contact2 = (Contact)null;
try
{
var firstName = ReadFirstName(plugin, pipelineStep, pipelineContext, logger);
var lastName = ReadLastName(plugin, pipelineStep, pipelineContext, logger);
var birthDate = ReadBirthDate(plugin, pipelineStep, pipelineContext, logger);
var queryable = client.Contacts.Where(c =>
c.GetFacet<PersonalInformation>(Sitecore.XConnect.Collection.Model.CollectionModel.FacetKeys
.PersonalInformation).FirstName == firstName &&
c.GetFacet<PersonalInformation>(Sitecore.XConnect.Collection.Model.CollectionModel.FacetKeys
.PersonalInformation).LastName == lastName &&
c.GetFacet<PersonalInformation>(Sitecore.XConnect.Collection.Model.CollectionModel.FacetKeys
.PersonalInformation).Birthdate == birthDate)
.WithExpandOptions(contactExpandOptions);
var enumerator = queryable.GetBatchEnumeratorSync(1);
while (enumerator.MoveNext())
{
return enumerator.Current;
}
}
catch (XdbExecutionException ex)
{
// Handle exception
return null;
}
}
return contact;
}
protected virtual string ReadFirstName(ContactFallbackResolverSettings plugin, PipelineStep pipelineStep, PipelineContext pipelineContext, ILogger logger)
{
XConnectContactIdentifierSettings xConnectContactIdentifierSettings = pipelineStep.GetPlugin<XConnectContactIdentifierSettings>();
object fromPipelineContext = this.GetObjectFromPipelineContext(xConnectContactIdentifierSettings.ContactIdentificationLevelObjectLocation, pipelineContext, logger);
if (fromPipelineContext != null)
{
var firstNameValueAccessor = plugin.FirstNameValueAccessor;
if (firstNameValueAccessor != null)
{
IValueReader valueReader = firstNameValueAccessor.ValueReader;
if (valueReader != null)
{
DataAccessContext context = new DataAccessContext();
ReadResult readResult = valueReader.Read(fromPipelineContext, context);
if (readResult != null && readResult.WasValueRead)
{
return readResult.ReadValue as string;
}
}
}
}
return null;
}
protected virtual string ReadLastName(ContactFallbackResolverSettings plugin, PipelineStep pipelineStep, PipelineContext pipelineContext, ILogger logger)
{
XConnectContactIdentifierSettings xConnectContactIdentifierSettings = pipelineStep.GetPlugin<XConnectContactIdentifierSettings>();
object fromPipelineContext = this.GetObjectFromPipelineContext(xConnectContactIdentifierSettings.ContactIdentificationLevelObjectLocation, pipelineContext, logger);
if (fromPipelineContext != null)
{
var firstNameValueAccessor = plugin.LastNameValueAccessor;
if (firstNameValueAccessor != null)
{
IValueReader valueReader = firstNameValueAccessor.ValueReader;
if (valueReader != null)
{
DataAccessContext context = new DataAccessContext();
ReadResult readResult = valueReader.Read(fromPipelineContext, context);
if (readResult != null && readResult.WasValueRead)
{
return readResult.ReadValue as string;
}
}
}
}
return null;
}
protected virtual DateTime? ReadBirthDate(ContactFallbackResolverSettings plugin, PipelineStep pipelineStep, PipelineContext pipelineContext, ILogger logger)
{
XConnectContactIdentifierSettings xConnectContactIdentifierSettings = pipelineStep.GetPlugin<XConnectContactIdentifierSettings>();
object fromPipelineContext = this.GetObjectFromPipelineContext(xConnectContactIdentifierSettings.ContactIdentificationLevelObjectLocation, pipelineContext, logger);
if (fromPipelineContext != null)
{
var firstNameValueAccessor = plugin.BirthDateValueAccessor;
if (firstNameValueAccessor != null)
{
IValueReader valueReader = firstNameValueAccessor.ValueReader;
if (valueReader != null)
{
DataAccessContext context = new DataAccessContext();
ReadResult readResult = valueReader.Read(fromPipelineContext, context);
if (readResult != null && readResult.WasValueRead)
{
return readResult.ReadValue as DateTime?;
}
}
}
}
return null;
}
}
}
ContactFallbackResolverSettings.cs
namespace PipelineBatchRunner
{
public class ContactFallbackResolverSettings : Sitecore.DataExchange.IPlugin
{
public Sitecore.DataExchange.DataAccess.IValueAccessor FirstNameValueAccessor { get; set; }
public Sitecore.DataExchange.DataAccess.IValueAccessor LastNameValueAccessor { get; set; }
public Sitecore.DataExchange.DataAccess.IValueAccessor BirthDateValueAccessor { get; set; }
}
}
The code above overrides a standard Resolve Contact by Identifier from xConnect Pipeline Step
behavior. If contact is not resolved by identifier, the extended pipeline step will try to resolve it by First Name
, Last Name
and Birth Date
.
Don't forget to update with corresponding namespaces _StandardValues of the Converter Type
and Processor Type
fields for the newly created pipeline step.
You probably can optimize the code. I've only tried to explain an idea.