6

I'm trying to create a Sitecore scheduled task to automatically ExecuteRightToBeForgotten for contacts that haven't had any interaction with the site for a number of days.

I can get the contacts I need, but after calling ExecuteRightToBeForgotten and Submitting the changes, an error is thrown:

ManagedPoolThread #5 11:51:03 INFO  Starting: ContactCleanup
ManagedPoolThread #5 11:51:03 INFO  Starting ContactCleanup task
ManagedPoolThread #5 11:51:07 ERROR Error executing command item: ContactCleanup
Exception: System.Reflection.TargetInvocationException
Message: Exception has been thrown by the target of an invocation.
Source: mscorlib
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Sitecore.Reflection.ReflectionUtil.InvokeMethod(MethodInfo method, Object[] parameters, Object obj)
   at Sitecore.Tasks.CommandItem.Execute(Item[] items, ScheduleItem schedule)

Nested Exception

Exception: System.AggregateException
Message: One or more errors occurred.
Source: Sitecore.Xdb.Common.Web
   at Sitecore.Xdb.Common.Web.Synchronous.SynchronousExtensions.SuspendContextLock[TResult](Func`1 taskFactory)
   at Sitecore.XConnect.Client.XConnectSynchronousExtensions.SuspendContextLock(Func`1 taskFactory)
   at Foundation.ContactCleanup.Tasks.ContactCleanupTask.CleanupContacts(Int32 days) in D:\projects\platform\src\Foundation\ContactCleanup\code\Foundation.ContactCleanup\Tasks\ContactCleanupTask.cs:line 53
   at Foundation.ContactCleanup.Tasks.ContactCleanupTask.Execute(Item[] items, CommandItem command, ScheduleItem schedule) in D:\projects\platform\src\Foundation\ContactCleanup\code\Foundation.ContactCleanup\Tasks\ContactCleanupTask.cs:line 24


ManagedPoolThread #5 11:51:07 ERROR Error executing schedule item: ContactCleanup
Exception: System.Reflection.TargetInvocationException
Message: Exception has been thrown by the target of an invocation.
Source: mscorlib
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Sitecore.Reflection.ReflectionUtil.InvokeMethod(MethodInfo method, Object[] parameters, Object obj)
   at Sitecore.Tasks.CommandItem.Execute(Item[] items, ScheduleItem schedule)
   at Sitecore.Tasks.ScheduleItem.Execute()

Nested Exception

Exception: System.AggregateException
Message: One or more errors occurred.
Source: Sitecore.Xdb.Common.Web
   at Sitecore.Xdb.Common.Web.Synchronous.SynchronousExtensions.SuspendContextLock[TResult](Func`1 taskFactory)
   at Sitecore.XConnect.Client.XConnectSynchronousExtensions.SuspendContextLock(Func`1 taskFactory)
   at Foundation.ContactCleanup.Tasks.ContactCleanupTask.CleanupContacts(Int32 days) in D:\projects\platform\src\Foundation\ContactCleanup\code\Foundation.ContactCleanup\Tasks\ContactCleanupTask.cs:line 53
   at Foundation.ContactCleanup.Tasks.ContactCleanupTask.Execute(Item[] items, CommandItem command, ScheduleItem schedule) in D:\projects\platform\src\Foundation\ContactCleanup\code\Foundation.ContactCleanup\Tasks\ContactCleanupTask.cs:line 24

My xDB is up and there are Contacts that are found to be forgotten (532 in total). Is this a thing with scheduled tasks not being able to edit contacts or something?

Here's the code that's being run by the scheduled task:

public class ContactCleanupTask 
{
    private const string CleanupAfterDays = "Foundation.ContactCleanup.CleanupAfterDays";

    public void Execute(Item[] items, Sitecore.Tasks.CommandItem command, Sitecore.Tasks.ScheduleItem schedule)
    {
        Log.Info("Starting ContactCleanup task", this);

        if (!int.TryParse(Settings.GetSetting(CleanupAfterDays), out var days))
        {
            days = 420; // 14 months
        }
        var result = CleanupContacts(days);

        Log.Info($"ContactCleanup finished, executed right to be forgotten for {result} contacts.", this);
    }

    private int CleanupContacts(int days)
    {
        int numberOfForgottenContacts = 0;
        using (var client = SitecoreXConnectClientConfiguration.GetClient())
        {
            try
            {
                // Query to find contacts without any interactions after set amount of days
                var query = client.Contacts
                    .Where(contact => !contact.Interactions.Any(interaction => interaction.EndDateTime > DateTime.UtcNow.AddDays(-days)));

                // Execute query and extract contacts
                var contactCursor = query.GetBatchEnumeratorSync(50);

                while (contactCursor.MoveNext(50))
                {
                    var currentBatch = contactCursor.Current;

                    foreach (var contact in currentBatch)
                    {
                        numberOfForgottenContacts++;
                        client.ExecuteRightToBeForgotten(contact);
                    }

                }

                client.Submit();
            }
            catch (XdbExecutionException ex)
            {
                Log.Error("Xdb Error while processing contacts to forget", ex, this);
            }

            return numberOfForgottenContacts;
        }
    }
}

Note; the catch isn't hit because it's not an xDbExecutionException, but the ContactCleanupTask.cs:line 53 from the log is the client.Submit(); call

Can you help me?

1 Answer 1

4

This is a known behavior of xConnect.

You can not use ExecuteRightToBeForgotteninside a batch enumerator.

Method ExecuteRightToBeForgotten looks like :

public static RightToBeForgottenOperation ExecuteRightToBeForgotten(this IXdbContext context, IEntityReference<Contact> contact)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    if (contact == null)
    {
        throw new ArgumentNullException("contact");
    }
    RightToBeForgottenOperation rightToBeForgottenOperation = new RightToBeForgottenOperation(contact);
    context.RegisterOperation(rightToBeForgottenOperation);
    return rightToBeForgottenOperation;
}

in the context.RegisterOperation(rightToBeForgottenOperation) you have :

public void RegisterOperation(IXdbOperation operation)
{
    if (!this.Configuration.IsDataExtractionEnabled && (operation is CreateContactCursorOperation || operation is CreateInteractionCursorOperation))
    {
        throw new XdbExecutionException("The data extraction is disabled for this client.");
    }
    this.CheckNotDisposed();
    if (this._executingBatchCount > 0)
    {
        throw new InvalidOperationException("Operations cannot be added to the context while it is executing. Add operations directly to the executing batch instead");
    }
    this.EnsureBatch();
    this._currentBatch.Add(operation, true);
}

If it's a batch operation InvalidOperationException is thrown . You need to add your contacts to a list and you need to use ExecuteRightToBeForgotten one by one for every contact.

You need to be careful with the WithExpandOptions . You can use use the parameter less variant

1
  • Apart from caching the resulting Contacts in a List and putting GetBatchEnumeratorSync in a using block, I also needed to add .WithExpandOptions(new ContactExpandOptions() with some custom facet keys to make sure ExecuteRightToBeForgotten had access to them to make it work all the way, but your help was the breakthrough I needed! Commented Mar 13, 2019 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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