2

Azure Table Storage does not support many property types (List<>, TimeSpan, etc).

There are solutions like Lucifure Stash and Lokad.Cloud, but they are not compiling for DNX Core 5.0.

Is there a way to add support for custom property types in Azure Tables with DNX Core?

1 Answer 1

6

One solution is to use reflection to iterate through all the “custom” properties of the entity and serialize them to JSON strings.

We can override TableEntity’s ReadEntity and WriteEntity methods to hook de-/serialization:

using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public abstract class CustomEntity : TableEntity
{
    public override IDictionary<string, EntityProperty> WriteEntity (OperationContext operationContext)
    {
        var properties = base.WriteEntity(operationContext);

        // Iterating through the properties of the entity
        foreach (var property in GetType().GetProperties().Where(property =>
                // Excluding props explicitly marked to ignore serialization
                !property.GetCustomAttributes<IgnorePropertyAttribute>(true).Any() &&
                // Excluding already serialized props
                !properties.ContainsKey(property.Name) &&
                // Excluding internal TableEntity props
                typeof(TableEntity).GetProperties().All(p => p.Name != property.Name)))
        {
            var value = property.GetValue(this);
            if (value != null)
                // Serializing property to JSON
                properties.Add(property.Name, new EntityProperty(JsonConvert.SerializeObject(value)));
        }

        return properties;
    }

    public override void ReadEntity (IDictionary<string, EntityProperty> properties, OperationContext operationContext)
    {
        base.ReadEntity(properties, operationContext);

        // Iterating through the properties of the entity
        foreach (var property in GetType().GetProperties().Where(property =>
                // Excluding props explicitly marked to ignore serialization
                !property.GetCustomAttributes<IgnorePropertyAttribute>(true).Any() &&
                // Excluding props which were not originally serialized
                properties.ContainsKey(property.Name) &&
                // Excluding props with target type of string (they are natively supported)
                property.PropertyType != typeof(string) &&
                // Excluding non-string table fields (this will filter-out 
                // all the remaining natively supported props like byte, DateTime, etc)
                properties[property.Name].PropertyType == EdmType.String))
        {
            // Checking if property contains a valid JSON
            var jToken = TryParseJson(properties[property.Name].StringValue);
            if (jToken != null)
            {
                // Constructing method for deserialization 
                var toObjectMethod = jToken.GetType().GetMethod("ToObject", new[] { typeof(Type) });
                // Invoking the method with the target property type; eg, jToken.ToObject(CustomType)
                var value = toObjectMethod.Invoke(jToken, new object[] { property.PropertyType });

                property.SetValue(this, value);
            }
        }
    }

    private static JToken TryParseJson (string s)
    {
        try { return JToken.Parse(s); }
        catch (JsonReaderException) { return null; }
    }
}

Now, if we inherit our table entities from the CustomEntity class, we can freely use properties with any types supported by Json.NET.

Sign up to request clarification or add additional context in comments.

3 Comments

We've noticed a problem with this code - the call to property.GetType() returns false for pretty much all properties as the type of property is Microsoft.WindowsAzure.Storage.Table.EntityProperty, so everything falls into the parser. TryParseJson then converts strings that look like Octal numbers into the decimal representation of the Octal.
@Zhaph-BenDuguid you’re right, thanks for the fix! I’m actually using this in production and made the same fix a long time ago, but forgot to update the code here, my bad. I’ve also added conditionals to skip de-/serialization of properties with the [IgnoreProperty] attribute.
Yep, it would appear we've done something similar with: !Attribute.IsDefined(property, typeof(JsonIgnoreAttribute))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.