Peter Löfman
Oct 22, 2016
  4666
(5 votes)

Force Property Type Reset

During the development phase of a project it is, at least for me, rather common to change property names and property types when refactoring or otherwise improving the code. This can create a clutter of properties on a content type that are not being used as well as property type mismatch since Episerver can ony handle some property type cconversions automatically. Episerver cannot for instance automatically convert a property that used to be a string to an int, which is very much understandable. And if you read up on refactoring content type classes Episerver states that: "If you need a change of [property] type and any data loss is acceptable, you can change the type of the property in admin mode, but you must temporarily remove the property from the code." 

Having to go into admin mode and temporarily remove property from code is too bothersome for me so instead of doing something that would take a minute or two I spent a couple of hours trying to write some code that will do this automatically, and here is what I came up with.

First a created an atribute (I really did this last but it sounds better like this) to be placed on the property that you have changed the type of and which cannot be converted automatically, for instance a string to an int.

[AttributeUsage(AttributeTargets.Property)]
public class ForceResetPropertyTypeAttribute : Attribute
{
}

And you place it on a property like this. And, please be aware that all previous data will be lost since we will delete the property and then add it again with the new type.

// I used to be a string but have been changed to an int
[ForceResetPropertyType]
[Display(GroupName = SystemTabNames.Content, Order = 100)]
public virtual int BackgroundColor { get; set; }

Next a made an initialization module that (1) deletes all properties that are missing in code and (2) scans each content type model for the attribute ForceResetPropertyTypeAttribute I create before, and simply deletes the property with all its data from Episerver and then re-adds it with the new property definition.

[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class ResetContentTypeslInitialization : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
        var propertyDefinitionRepository = ServiceLocator.Current.GetInstance<IPropertyDefinitionRepository>();
        var contentTypeModelRepository = ServiceLocator.Current.GetInstance<ContentTypeModelRepository>();
        var propertyDefinitionSynchronizer =
            (PropertyDefinitionSynchronizer)ServiceLocator.Current.GetInstance<IPropertyDefinitionTypeResolver>();

        // foreach (var contentType in contentTypeRepository.List()) - takes all content types 
        // and not just yours and will actually remove to much
        foreach (
            var contentType in
                contentTypeRepository.List()
                    .Where(ct => !string.IsNullOrEmpty(ct.ModelTypeString)
                        && ct.ModelTypeString.StartsWith("Your.Assembly.Containing.The.Content.Types")))
        {
            // First we delete all properties that do not exist in code - probably due to renaming them
            var propertyDefinitionsThatDoNotExistInCode =
                contentType.PropertyDefinitions.Where(
                    propertyDefinition => IsMissingModelProperty(propertyDefinition, contentTypeModelRepository));

            foreach (var propertyDefinition in propertyDefinitionsThatDoNotExistInCode)
            {
                propertyDefinitionRepository.Delete(propertyDefinition);
            }

            var modelType = contentType.ModelType;

            if (modelType == null) continue;

            // Next we want to check if there are any content types that have a property with the 
            // ForceResetPropertyTypeAttribute placed on them, and if so delete and re-add the property
            // definition.
            foreach (var propertyInfo in modelType.GetProperties())
            {
                var forceResetPropertyTypeAttribute =
                    propertyInfo.GetCustomAttribute<ForceResetPropertyTypeAttribute>();

                if (forceResetPropertyTypeAttribute == null) continue;

                var propertyName = propertyInfo.Name;

                var contentTypeModel = contentTypeModelRepository.GetContentTypeModel(modelType);
                var propertyDefinition = contentType.PropertyDefinitions.Single(pd => pd.Name.Equals(propertyName));
                var propertyDefinitionModel =
                    contentTypeModel.PropertyDefinitionModels.Single(p => p.Name.Equals(propertyName));

                // We check the current property definition type to see if perhaps we have already made the conversion.
                // We do not want to remove data on an already converted property
                var propertyDefinitionType = propertyDefinitionSynchronizer.ResolveType(propertyDefinitionModel);
                   
                if (propertyDefinition.Type.DefinitionType == propertyDefinitionType.DefinitionType) continue;

                // And finally here we delete the property definition and then re-adds it according to the new information
                // Please be aware that this will delete all the data as well.
                propertyDefinitionRepository.Delete(propertyDefinition);
                propertyDefinitionSynchronizer.CreatePropertyDefinition(propertyDefinitionModel, contentType.ID);
            }
        }
    }

    private static bool IsMissingModelProperty(
        PropertyDefinition propertyDefinition,
        ContentTypeModelRepository contentTypeModelRepository)
    {
        if (propertyDefinition == null) return false;

        if (!propertyDefinition.ExistsOnModel) return true;

        return contentTypeModelRepository.GetPropertyModel(propertyDefinition.ContentTypeID, propertyDefinition)
                == null;
    }

    public void Uninitialize(InitializationEngine context)
    {
    }
}

[Pasting files is not allowed][Pasting files is not allowed]

Oct 22, 2016

Comments

Daniel Ovaska
Daniel Ovaska Oct 22, 2016 10:30 AM

Looks sweet! Nice work!

Per Nergård (MVP)
Per Nergård (MVP) Oct 22, 2016 12:39 PM

Did something similiar a while back but not with the automatically propertytype switch attribute which I really think is nice.

Nov 1, 2016 01:38 PM

Well, this is embarrasing but my code actually "destroys" the Episerver Forms content type model FormContainerBlock, or at least it removes to vital actor properties from it because they are not part of the code of the model but rather, I guess, added during initialization. 

So, when using this you will need to make sure you are only doing it on your content types, and one way of doing this is to check the content type model type string and make sure it starts with your assembly name. 

Change:

foreach (var contentType in contentTypeRepository.List())

To:

foreach (var contentType in contentTypeRepository.List().Where(ct => !string.IsNullOrEmpty(ct.ModelTypeString) && ct.ModelTypeString.StartsWith("Your.Assembly.Containing.The.Content.Types")))

Please login to comment.
Latest blogs
Optimizely Commerce vs Composable Commerce: What Should You Do with CMS 13?

As organizations modernize their digital experience platforms, a common architectural question emerges: Should we continue using Optimizely Commerc...

Aniket | Mar 12, 2026

Missing Properties tool for Optimizely CMS

If you have been working with Optimizely CMS for a while you have probably accumulated some technical debt in your property definitions. When you...

Per Nergård (MVP) | Mar 10, 2026

AI Generated Optimizely Developer Newsletter

Updates in the Optimizely ecosystem are everywhere: blog posts, forums, release notes, NuGet packages, and documentation changes. This newsletter...

Allan Thraen | Mar 10, 2026 |

Lessons from Building Production-Ready Opal Tools

AI tools are becoming a normal part of modern digital platforms. With  Optimizely Opal , teams can build tools that automate real tasks across the...

Praful Jangid | Mar 7, 2026

My Takeaway from Optimizely Opal Agents in Action 2026 - What Agentic AI Means for the Future of Digital Marketing

I would like to share with you what stayed in my head after this amazing virtual event organized by Optimizely. Agents in Action 2026 , a live...

Augusto Davalos | Mar 6, 2026

From Vision to Velocity: Introducing the Optimizely MVP Technical Roundtable

Digital transformation is a two-sided coin. On one side, you have the high-level strategy, the business cases, the customer journeys, and the...

Patrick Lam | Mar 6, 2026