6

I'm creating a contact programmatically (outside of a user context) and saving it to the xDB usnig the following code:

public void EnsureUser(string email)
{
    var contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager;
    var contact = contactManager.CreateContact(Guid.NewGuid());
    contact.Identifiers.Identifier = email;
    // Set some other data on the contact
    contactManager.SaveAndReleaseContactToXdb(contact);
}

However, I would like to ensure that

  1. if a user with the supplied email already exists, the newly created contact is merged with the existing one, and
  2. the contact should be marked as a "known" contact in xDB

Normally I would use Tracker.Current.Session.Identify() to achieve this, but since I am not in a user context I cannot use the Tracker.

Is there a different way to identify a contact and start a merge process?

5

Below is what I believe the proper way of doing this. Note that, since you're not in the context of a live session, some Sitecore mechanisms (such as cross-cluster session migration) aren't available to you. Hence, you must guard against edge cases, particularly when the contact with given identifier already exists and is currently locked by another process.

Since the contact you're creating doesn't exist in xDB yet, there's no need for an actual merge. What you need to do is get the existing contact and update its fields.

public void EnsureUser(string email)
{
    LeaseOwner leaseOwner = new LeaseOwner("YOUR_WORKER_NAME", LeaseOwnerType.OutOfRequestWorker);

    ContactRepositoryBase contactRepository = Factory.CreateObject("contactRepository", true) as ContactRepositoryBase;

    // Attempt to obtain an exclusive lock on an existing contact in xDB.
    LockAttemptResult<Contact> lockResult = contactRepository.TryLoadContact(email, leaseOwner, TimeSpan.FromMinutes(1));

    Contact contact = null;

    if (lockResult.Status == LockAttemptStatus.AlreadyLocked)
    {
        // Another worker or a live web session has an exclusive lock on the contact.
        // You can't use this contact right now. It's up to you what to do in this case.
        /* ... */
    }
    else if (lockResult.Status == LockAttemptStatus.DatabaseUnavailable)
    {
        // Database is down. Try to handle this gracefully.
        /* ... */
    }
    else if (lockResult.Status == LockAttemptStatus.NotFound)
    {
        // A contact with the given identifier doesn't exist.
        // Just create a new contact object.
        contact = contactRepository.CreateContact(Guid.NewGuid());

        // Identify it.
        contact.Identifiers.Identifier = email;

        // And make it known.
        contact.Identifiers.IdentificationLevel = Sitecore.Analytics.Model.ContactIdentificationLevel.Known;
    }
    else
    {
        // We successfull locked an existing contact.
        contact = lockResult.Object;
    }

    // Set some other data on the contact:
    /* ... */

    // Save the contact and release the lock.
    if (contact != null)
    {
        var options = new ContactSaveOptions(release: true, owner: leaseOwner);
        contactRepository.SaveContact(contact, options);
    }
}

Note that instead of the Contact Manager, I used the Contact Repository in the above code, since it works directly with the xDB storage. The Contact Manager in your sample code queries and saves contacts in the Shared Session first, and only then accesses xDB via the underlying Contact Repository.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.