Sitecore – Dependency injection in Marketing Automation Conditions

When creating custom conditions using the Sitecore.Framework.Rules.ICondition interface you might have run into the problem that there’s no way to make use of Depdency Injection. If you add parameters to the constructor, you’ll get exceptions that the object can not be created, and there’s no ServiceLocator pattern implemented.

The creation of the ICondition implementations is done by using the Activator.CreateInstance, so it requires an parameterless constructor. Luckily for us, there’s a small change to be made, so we can can get a reference to the IServiceProvider and use that for our dependency resolving.

For an example on how to register services within XConnect, please see my other blog post, and keep in mind to surround the Services tag with <MarketingAutomation><Engine>, as you can see in the configuration example below.

Condition Evaluation Service

To create instances of the Condition and evaluating it, Sitecore uses the Sitecore.Xdb.MarketingAutomation.Rules.ConditionEvaluationService. This class is responsible for creating instances of the Rule and passing the required facts to the condition.

We can override this class, and add the IServiceProvider as a fact, so we can use it in the ICondition. Please see the example code below.

using System;
using Microsoft.Extensions.Logging;
using Sitecore.Framework.Rules;
using Sitecore.XConnect.Segmentation.Predicates;
using Sitecore.Xdb.MarketingAutomation.Core.Processing.Plan;
using Sitecore.Xdb.MarketingAutomation.Core.Rules;
using Sitecore.Xdb.MarketingAutomation.Rules;

namespace Example
{
    public class CustomConditionEvaluationService : ConditionEvaluationService
    {
        private readonly IServiceProvider _serviceProvider;

        public CustomConditionEvaluationService(ILogger<ConditionEvaluationService> logger, IServiceProvider serviceProvider) : base(logger, serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        protected override void PopulateScopedFacts(FactSpecificier facts, IContactProcessingContext processingContext,
            IConditionServices conditionServices, ISegmentationServiceContext segmentationServiceContext)
        {
            base.PopulateScopedFacts(facts, processingContext, conditionServices, segmentationServiceContext);
            facts.Fact(_serviceProvider);
        }
    }
}

You can now register this class by adding a new config file to your Marketing Automation service. Make sure that this config file is placed in App_Data\jobs\continuous\AutomationEngine\App_Data\Config\sitecore\MarketingAutomation, and make sure that it starts with sc.Z. (the sc part is required for Sitecore to pick it up, and the Z is required so that this file is loaded after the Sitecore configuration files).

The configuration file should be as follows:

<Settings>
  <Sitecore>
    <XConnect>
      <MarketingAutomation>
        <Engine>
          <Services>
            <!-- The condition evaluation service -->
            <MarketingAutomation.Rules.ConditionEvaluationService>
              <Type>Example.CustomConditionEvaluationService, Example</Type>
              <As>Sitecore.Xdb.MarketingAutomation.Core.Rules.IConditionEvaluationService, Sitecore.Xdb.MarketingAutomation.Core</As>
              <LifeTime>Singleton</LifeTime>
            </MarketingAutomation.Rules.ConditionEvaluationService>
          </Services>
        </Engine>
      </MarketingAutomation>
    </XConnect>
  </Sitecore>
</Settings>

Using the IServiceProvider in an ICondition

Within your ICondition implementation, you can now context.Fact to get the IServiceProvider. Please see the example below:

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Sitecore.Framework.Rules;

namespace Example
{
    public class HasRegisteredAtLocation : ICondition
    {
        public bool Evaluate(IRuleExecutionContext context)
        {
            var serviceProvider = context.Fact<IServiceProvider>();
            if (serviceProvider == null)
            {
                throw new ArgumentNullException("ServiceProvider");
            }

            //You can now do something with your ServiceProvider, for example getting an ILogger
            serviceProvider.GetService<ILogger>();

            return true;
        }
    }
}

Leave a Reply

Your email address will not be published.