Beware when storing complex types in PropertyList!

Feb 09, 2026 by Stefan Holm Olsen

In Optimizely CMS we can easily store lists of both simple and complex types. This is easy to manage, shows nicely in the editor interface and it is stored and loaded efficiently.

It is both supported and documented on Optimizely World, for CMS and for Commerce.

However, there is a performance problem in those published code samples. The PropertyList does not perform equality comparison unless we tell it how.

An example

Let's consider an example from the real world.

In a Commerce Connect site, we might store a list of product specifications on each product or variant page. Those are typically key-value items of a variable number. So, we might as well store them as a custom property list.

This Commerce Connect site would probably also synchronize catalog data from an external PIM or MDM system.

To keep it very simple, I preload all related catalogue items from the Commerce catalogue. For each incoming product or variant, I then look up the same item from the preloaded items. Then I assign the incoming values to the catalogue properties.

Here is a very small cut-out from such an import job.

var variationClone = variationContent.CreateWritableClone<VariationPage>();
variationClone.Color = colorFromPim;
variationClone.Specifications = specificationsFromPim
.Select(x => new SpecificationItem { Key = x.Key, Value = x.Value })
.ToList();
if (variationClone.IsModified)
{
_contentRepository.Publish(variationContent);
}

When will a variation in this sample be saved and published?

Always. Because variationClone.IsModified will always be true.

This is because the default implementation of a custom property list, will use the default equality comparer. And that only considers whether the two variables are equal (as in pointing to the same memory location). Even if their contents look the same.

This results in unnecessary new content versions, with no real differences, every time the job is run.

Solution

An easy solution is to create a custom comparer for each custom list property.

In my example, the property list is storing this simple SpecificationItem record.

public record SpecificationItem
{
public string Key { get; set; }
public string Value { get; set; }
}

Here is my corresponding property type implementation. Notice how it overrides the ItemComparer property.

[PropertyDefinitionTypePlugIn]
public class PropertySpecificationList : PropertyList<SpecificationItem>
{
// Use our own item comparer implementation, which specifically check whether the property values are equal.
protected override IEqualityComparer<SpecificationItem> ItemComparer => SpecificationItemComparer.Default;
}

And finally, this is where the magic happens.

public class SpecificationItemComparer : IEqualityComparer<SpecificationItem>
{
// Initialize a static field for easy reuse.
public static readonly SpecificationItemComparer Default = new();

// Depending on your needs use string.Equals with explicit StringComparison value, instead of "==".
public bool Equals(SpecificationItem x, SpecificationItem y) =>
ReferenceEquals(x, y) || (x is not null && y is not null && x.Key == y.Key && x.Value == y.Value);

public int GetHashCode(SpecificationItem obj) =>
HashCode.Combine(obj.Key, obj.Value);
}

This solution works equally well in Optimizely CMS and Commerce Connect.

.Net Performance Optimizely Optimizely CMS