Generic EditorDescriptor for custom SelectionFactories

We are extensive users of the feature of implementing custom selection factories by using the EditorDescriptor-attribute in Episerver.

The use-case is quite simple and provides the editor with the ability to single select a value within multiple alternatives using a drop down. But as the selection stores grow, the projects gets unnecessary bloated with duplicate code.

Here's a take on that.

Key/Values for the win

First we need to abstract some things. The ISelectionFactory can easily be translated to a key-value store, and the ISelectItem object as a key/value item. (documentation)

Therefore, the Dictionary<string, string> is optimal in this use case.
Although this solution is fairly straight forward, this is the "mind-twisting" aspect.

Our Key will be our Value when binding our ISelectItem with our Dictionary<string, string>.

Why? Because it's the points in each object which need to be unique.

Notation Dictionary ISelectItem
Need to be unique Key ISelectItem.Value
Generic Value ISelectItem.Text

In Episerver, the values of the "Text"-field is used as presentation, and "Value" as the value which is aimed to be saved in our data store.

Our custom selection factory

Our generic but custom selection factory takes a generic object of T which we assume is of type Dictionary<string, string>. This is a good point to implement some logging and error handling.

When we have a valid instance, simply loop through the items and yield as your common SelectionFactory would.

Please note the binding of SelectionItem.Value = Setting.Key as described previously.

The Editor Descriptor

Now create a generic EditorDescriptor which allows us to provide a custom selection factory.

SelectionFactoryType = typeof(CustomSelectionFactory<TSelectionFactory>) is used to point out our custom selection factory. The value passed via the attribute is passed down to our CustomSelectionFactory object.

The Editor Descriptor attribute

The fun part with the built-in EditorDescriptor attribute is that we can define a new type with the EditorDescriptionType argument.

This means that we can call it like this: [EditorDescriptor(EditorDescriptorType = typeof(CustomSelectionFactoryEditorDescriptor<TSelectionFactory>))] to decorate our property on our Episerver model.

Bind with data

A trivial example to bind this newly created functionality with some fine data is to create a class which inherits Dictionary<string, string>.

And then simply decorate our property like this:

[EditorDescriptor(EditorDescriptorType = typeof(CustomSelectionFactoryEditorDescriptor<SelectionSettings>))]
public virtual string MySelector { get; set; }  

The fun part with this is that you simply can take an existing data source, such as an XML-file or a JSON data object, in your project and reuse as a selection factory. All you need to do is to create a new "SelectionSettings" class or bind a current object inheriting Dictionary<string, string> and rock and roll.

Closing comments

When writing this, several alternative solutions come to mind, but all have one thing in common; it all requires an agreement on the data structure and simply boils down to a key/value store.

In our implementation we also have overwritten the epi-cms/contentediting/editors/SelectionEditor with our own so we can pass HTML-formatted values via our selection factories. See blogpost "Make Episerver SelectionEditor support HTML-labels" for further implementation.