Don't miss out Virtual Happy Hour this Friday (April 26).

Try our conversational search powered by Generative AI!

Peter Löfman
Oct 22, 2016
  3289
(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

Oct 22, 2016 10:30 AM

Looks sweet! Nice work!

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
Solving the mystery of high memory usage

Sometimes, my work is easy, the problem could be resolved with one look (when I’m lucky enough to look at where it needs to be looked, just like th...

Quan Mai | Apr 22, 2024 | Syndicated blog

Search & Navigation reporting improvements

From version 16.1.0 there are some updates on the statistics pages: Add pagination to search phrase list Allows choosing a custom date range to get...

Phong | Apr 22, 2024

Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog