Remapping Optimizely Dynamic Data Store


Optimizely DDS is a powerful feature for storing and retrieving custom .NET objects. Adding new properties to an object type class that is missing mapping attributes can be a challenge, but by following the steps outlined in this blog post, you can easily remap the class and its properties to the DDS and its existing items.

Estimated read time : 6 minutes

Jump to

Key takeaways

  • Remap DDS object types
  • Best practice for DDS

What is Optimizely Dynamic Data Store?

Optimizely Dynamic Data Store (DDS) is an essential feature for storing and retrieving custom .NET objects in the Optimizely database table Big Table. It is useful for when you want to save smaller content or data models that are not suited to be an Optimizely content type.

The custom .NET object type and its properties are mapped against columns in the database table.

Best practice

As a best practice, the object type class should always inherit from IDynamicData and be decorated with the attributes [EPiServerDataStore] and [EPiServerDataContract].

The class properties should be decorated with the [EPiServerDataMember] attribute. This ensures that the class is properly mapped to the database table and that any new properties are mapped automatically.

The problem

If you have a class that is not decorated with any mapping attributes for DDS, you might encounter problems when adding a new property. The new property will not be mapped to the DDS, and it will not be possible to save any value to the property.

This can be frustrating, especially if there are thousands of DDS items for the class saved in the database table, making it impossible to create a new class or delete existing items and start anew.

That was the case for me with an undecorated class called Comment and was being used to save user comments for news pages on a website. 

public class Comment
{
    public Comment()
    {
        Id = Identity.NewIdentity(Guid.NewGuid());
        ContentId = 0;
        Name = string.Empty;
        CommentText = string.Empty;
    }

    public Comment(string username, ContentReference commentedPage, string message)
    {
        Id = Identity.NewIdentity(Guid.NewGuid());
        ContentId = commentedPage.ID;
        Name = username;
        CommentText = message;
        Time = DateTime.UtcNow;
    }

    public Identity Id { get; set; }
    public int ContentId { get; set; }
    public string Name { get; set; }
    public string CommentText { get; set; }
    public DateTime Time { get; set; }
}

I wanted to add a new boolean property called Highlight but its value was not saved in the database table despite restarts and cache clearings.

The solution

To solve this problem, refactor the class to inherit from IDynamicData and decorate it with the attributes [EPiServerDataStore] and [EPiServerDataContract].

Set the parameters AutomaticallyRemapStore and AutomaticallyCreateStore for [EPiServerDataStore] attribute to "true" to ensure smoother operations. Set the parameter StoreName to the same name being used in the database table to prevent a new store name being created.

Then add the attribute [EPiServerDataMember] to every property of the class. This ensures that the class and its properties are fully mapped against the columns in the database table.

[EPiServerDataStore(
    AutomaticallyRemapStore = true,
    AutomaticallyCreateStore = true,
    StoreName = "CommentsForPages"
)]
[EPiServerDataContract]
public class Comment : IDynamicData
{
    public Comment()
    {
        Id = Identity.NewIdentity(Guid.NewGuid());
        ContentId = 0;
        Name = string.Empty;
        CommentText = string.Empty;
        Hightlight = false;
    }

    public Comment(string username, ContentReference commentedPage, string message)
    {
        Id = Identity.NewIdentity(Guid.NewGuid());
        ContentId = commentedPage.ID;
        Name = username;
        CommentText = message;
        Hightlight = false;
        Time = DateTime.UtcNow;
    }

    [EPiServerDataMember]
    public Identity Id { get; set; }

    [EPiServerDataMember]
    public int ContentId { get; set; }

    [EPiServerDataMember]
    public string Name { get; set; }

    [EPiServerDataMember]
    public string CommentText { get; set; }

    [EPiServerDataMember]
    public DateTime Time { get; set; }

    [EPiServerDataMember]
    public bool Hightlight { get; set; }
}

After refactoring the class, the next step is to remap the class and its properties to the DDS and its existing items. By using an initialization module, the remapping will be done at startup and not require any scheduled jobs or custom admin tools.

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class CommentsRemapInitializationModule : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        try
        {
            var store = DynamicDataStoreFactory.Instance.GetStore("CommentsForPages");

            store.StoreDefinition.Remap(typeof(Comment));
            store.StoreDefinition.CommitChanges();
            store.Refresh();
        }
        catch (Exception ex)
        {
            // Log the error
        }
    }

    public void Uninitialize(InitializationEngine context) { }
}

The initialization module will fetch the store object from DDS, remap the store definition to the class type, commit the remap changes, and refresh to clear cache. This will ensure that the store definition has been remapped to the class type, and the newly added property and its value will be saved in the database table.

Summary

Adding new properties to an object type class that is missing mapping attributes can be a challenge, but by adding attributes to the class and creating a remap initialization module you can easily remap the class and its old and new properties to the DDS and its existing items.