2

In DEF 4.0 and SC 9.3, using this Sitecore guide I created a custom Value Expression Converter for a new filter so that I can filter generic Events based on the DefinitionId, in this case Print events:

Custom Filter Expression

Expression Converter

This custom expression converter was based off a similar converter that ships with DEF:

EventTypeExpressionConverter

I have confirmed that the library reference in the Converter Type field is valid, and when I run the pipeline, I see the base constructor getting called inside of my custom converter (first break point), but I never see any other code being called (second break point). This is obvious because Events are being synced that shouldn't be based on the filters that I have specified.

The high level flow is this:

  • Pipeline: Process Single Interaction from xConnect
    • Pipeline Step: Read Page Events from Interaction
      • Source Object Value Accessor: "Data Access/Value Accessors/Providers/xConnect/Page Events from Interaction"
      • Filter: "Filter Expressions/Event Filter Expressions/Print Event Expression"
        • Condition Operator: “Equal”
        • Right Value Accessor: "DefinitionId on xConnect Event"
        • Value: "Page Events/Print" ({CD52B756-21B4-4028-8BA5-E981B8A96F95})
        • Converter Type: "Client.Foundation.DataExchange.Providers.xConnect.Expressions.GenericEventDefinitionTypeValueExpressionConverter, Client.Foundation.DataExchange"

2 Answers 2

2

I can't see it from the screenshot, do you specify expression converter item template ID in the [SupportedIds] attribute for your custom Expression Converter class?

If not, the constructor of this class will be called, but BaseItemModelConverter<> will never reach its method ConvertSupportedItem(). Here is how it works internally:

  • when an instance of a converter class is created, its constructor gets a collection of supported template IDs from the attribute [SupportedIds]
  • then the method Convert() of BaseItemModelConverter<> is executed and it checks that the passed source item template ID exists in the list of supported IDs
    • if yes, the overridden method ConvertSupportedItem() will be called
    • if not, Convert() will return a negative result and ConvertSupportedItem() will not run

So your custom Expression Converter class definition should look similar to this:

[SupportedIds("{00000000-0000-0000-0000-000000000000}")]
public class FilterExpressionConverter : BaseItemModelConverter<FilterExpressionDescriptor>
{
...
}
2
  • 1
    This is a great explanation but I don't think that it applies in this case -- if we take a look at the BaseItemModelConverter, it has a method called IsSupportedItem, whereby a check is done for this.IgnoreValidation || !this.SupportedTemplateIds.Any<Guid>() meaning that if we set IgnoreValidation to true (which I have tried), or if the class has no SupportedIds specified, the source item is considered to be a supported item. None of the other default expression converters I looked at specified any SupportedIds; for example, the ContactIdentifierTypeValueExpressionConverter. Aug 10, 2022 at 22:13
  • @MarcelGruber that's interesting, I completely overlooked the line this.IgnoreValidation || !this.SupportedTemplateIds.Any<Guid>(). But you are right, the lack of SupportedIds attribute should not cause this problem and I can't see any other reason why it skips ConvertSupportedItem(). Maybe you can try to override the method IsSupportedItem(ItemModel source) to check if it even reaches this point?
    – Anna Gevel
    Aug 11, 2022 at 7:38
0

It has been quite a while since I worked on this, but I did get it working. If I recall correctly, the issue was in the code and not in DEF settings items, and the reason it wasn't getting picked up was because a reference to an interface or class needed to be updated. This combination of code worked:

public class EventDefinitionIdValueExpressionBuilder : BaseEventDefinitionTypeExpressionBuilder<Guid>
{
    public IValueAccessor RightValueAccessor { get; set; }

    public Guid EventDefinitionType { get; set; }

    protected override Expression<Func<Event, bool>> BuildEventExpression(
      ExpressionContext expressionContext)
    {
        Guid eventDefinitionType = this.GetValue(expressionContext);
        switch (this.ExpressionType)
        {
            case ExpressionType.Equal:
                return (Expression<Func<Event, bool>>)(ev => ev.DefinitionId.Equals(eventDefinitionType));
            case ExpressionType.NotEqual:
                return (Expression<Func<Event, bool>>)(ev => !ev.DefinitionId.Equals(eventDefinitionType));
            default:
                throw new NotSupportedException(string.Format("Condition operator is not supported. (Condition operator: {0})", (object)this.ExpressionType));
        }
    }

    protected override Guid GetValue(
      ExpressionContext expressionContext)
    {
        if (this.RightValueAccessor?.ValueReader != null)
        {
            ReadResult readResult = this.RightValueAccessor.ValueReader.Read((object)expressionContext, new DataAccessContext());
            if (readResult.WasValueRead)
            {
                return readResult.ReadValue as Guid? ?? Guid.Empty;
            }
        }
        return this.EventDefinitionType;
    }
}
public class EventDefinitionIdValueExpressionConverter : BaseItemModelConverter<IEventExpressionBuilder>
{
    public const string FieldNameConditionOperator = "ConditionOperator";
    public const string FieldNameValue = "Value";
    public const string FieldNameRightValueAccessor = "RightValueAccessor";
    protected readonly ExpressionTypeConverter ExpressionTypeConverter;

    public EventDefinitionIdValueExpressionConverter(IItemModelRepository repository) : this(repository, new ExpressionTypeConverter())
    {
    }

    public EventDefinitionIdValueExpressionConverter(
        IItemModelRepository repository,
        ExpressionTypeConverter expressionTypeConverter)
        : base(repository)
    {
        this.ExpressionTypeConverter = expressionTypeConverter ?? new ExpressionTypeConverter();
    }

    protected override ConvertResult<IEventExpressionBuilder> ConvertSupportedItem(ItemModel source)
    {
        string conditionOperator = this.GetConditionOperator(source);
        ExpressionType expressionType;

        if (!this.TryGetExpressionType(conditionOperator, out expressionType))
        {
            return this.NegativeResult(source, "Cannot convert string condition operator to expression type. (condition operator: " + conditionOperator + ")");
        }

        Guid eventDefinitionId = this.GetEventDefinitionId(source);
        IValueAccessor rightValueAccessor = this.GetRightValueAccessor(source);

        EventDefinitionIdValueExpressionBuilder expressionBuilder = new EventDefinitionIdValueExpressionBuilder();

        expressionBuilder.EventDefinitionType = eventDefinitionId;
        expressionBuilder.ExpressionType = expressionType;
        expressionBuilder.RightValueAccessor = rightValueAccessor;

        return this.PositiveResult((IEventExpressionBuilder)expressionBuilder);
    }

    protected virtual IValueAccessor GetRightValueAccessor(ItemModel source) => this.ConvertReferenceToModel<IValueAccessor>(source, "RightValueAccessor");

    protected virtual Guid GetEventDefinitionId(ItemModel source)
    {
        return this.GetGuidValue(source, "Value");
    }

    protected virtual string GetConditionOperator(ItemModel source) => this.GetReferenceAsModel(source, "ConditionOperator")["ItemName"].ToString();

    protected bool TryGetExpressionType(string conditionOperator, out ExpressionType expressionType)
    {
        return Enum.TryParse<ExpressionType>(conditionOperator, true, out expressionType);
    }
}
public class EventDefinitionIdValueReaderConverter : BaseItemModelConverter<IValueReader>
{
    private static EventDefinitionIdValueReader _reader = new EventDefinitionIdValueReader();

    public EventDefinitionIdValueReaderConverter(IItemModelRepository repository): base(repository)
    {
    }

    protected override ConvertResult<IValueReader> ConvertSupportedItem(
      ItemModel source)
    {
        return this.PositiveResult((IValueReader)EventDefinitionIdValueReaderConverter._reader);
    }
}
public abstract class BaseEventDefinitionTypeExpressionBuilder<TValue> : IEventExpressionBuilder
{
    public ExpressionType ExpressionType { get; set; }

    public Expression<Func<Event, bool>> Build(ExpressionContext expressionContext)
    {
        Expression<Func<Event, bool>> expression = this.BuildEventExpression(expressionContext);

        return Expression.Lambda<Func<Event, bool>>(expression.Body, (IEnumerable<ParameterExpression>)expression.Parameters);
    }

    protected abstract Expression<Func<Event, bool>> BuildEventExpression(ExpressionContext expressionContext);

    protected abstract TValue GetValue(ExpressionContext expressionContext);
}

For supplementary material, I have more code examples and explanations on these posts:

Your Answer

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

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