Retrieving meta data from PropertyData

Vote:
 

Is it possible to retrieve type information from a page/block type class from a PropertyData instance? I need a way to get the enum type (OpeningHoursType) from the OpenType property 

public class OpeningHoursWithDetailsBlock : BlockData
{
   [BackingType(typeof(PropertyNumber))]
   public virtual OpeningHoursType OpenType { get; set; }
   ...
}

All I see when looking through my PropertyData instance is about the backing type (PropertyNumber) but I really need the original enum type. Same problem with the corresponding PropertyDefinition.

Thanks in advance!

Emil

#120144
Apr 10, 2015 16:15
Vote:
 

Hi Emil!

What's the use case that you are trying to achieve and why can't you work with the model directly?

#120257
Apr 14, 2015 10:29
Ted
Vote:
 

Accidentally removed my previous post, and editing it didn't bring it back... :)

Perhaps you could do it with reflection like this:

var propertyName = "OpenType"; // Could be from your PropertyDefinition object instead

var type = typeof(OpeningHoursWithDetailsBlock).GetProperty(propertyName).PropertyType; // This would be your enum type

But if you already have a model instance:

var type = myBlock.OpenHours.GetType(); // This would be your enum type
#120275
Edited, Apr 14, 2015 13:40
Vote:
 

Hi Linus and Ted,

Thanks for taking interest :-)

The use case we have is a function that walks through pages and their properties recursively to find recent changes. We started out writing this code to be totally agnostic of page and block types and got into trouble with enum properties whose value we want to log as the actual enum constant rather than an integer value. Since we don't know the type at compile time we cannot do type casts and similar and it turned out not to be possible to retrieve all the information directly from a PropertyData. We ended up doing something partly similar to your suggestion, Ted. I'll describe the solution briefly using some pseudo-like code (the full code base is too large for pedagogic purposes).

If we keep track of the containing type in our tree walker, we can use the type together with a PropertyData instance and reflection to do this (error handling removed for brevity):

public static bool IsEnum(this PropertyData propertyData, Type containingType)
{
    var pi = containingType.GetProperty(propertyData.Name);
    return pi.PropertyType.IsEnum;
}

public static object GetEnumPropertyValue(this PropertyData propertyData, Type containingType)
{
    var type = containingType.GetProperty(propertyData.Name).PropertyType;
    return type.GetEnumName(propertyData.Value);
}

These two can then be used like this:

public void IterateOverProperties(IContentData content, Type containingType)
{
    foreach (var property in content.Property)
    {
        if (property.Value is BlockData)
        {
           var pi = context.ContainingType.GetProperty(property.Name);
           var newContainingType = pi.PropertyType;

           // Recurse ...
           IterateOverProperties(property.Value as BlockData, newContainingType)
        }
        else if (property.IsEnum(containingType) {
           LogProperty(property.Name, property.GetEnumPropertyValue(containingType));
        }
        else {
           LogProperty(property.Name, property.Value);
        }
    }

}

The last problem to solve is to pass the correct containingType when recursing into referenced blocks, the simple version for local blocks is shown above. For blocks in ContentArea properties it gets a little more complicated, we're currently looking at something like this:

var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();

foreach (var item in contentArea.Items)
{
    var content= item.GetContent();
    var contentType = contentTypeRepository.Load(content.ContentTypeID);

    // Recurse ...
    IterateOverProperties(content, contentType.ModelType);
}

Since we don't know the type beforehand we must load the content type using IContentTypeRepository and use its ModelType property. This seems to work.

Our real code, based on these ideas seem to work now, but feel free to comment if you feel there are better ways of doing it :-)

/Emil

#120374
Edited, Apr 15, 2015 22:32