My answer extends Zhaoxing's approach of writing the complex entity property to a JSON and persisting that to Azure CosmosDB.
However, serialization between a string and object in the setter causes the following issues:
- If, for example, you were to add or remove an item from your dictionary DicProperty, its setter would not get called since you have not modified the dictionary but have modified its contents. Similarly, in more complex use cases where you're interested in serializing custom objects or classes, modifying a member of the class will not trigger the setter. This could result in data being lost when the entity is committed to the CloudTable.
- If you do choose to implement something like INotifyPropertyChanged on your complex properties, either by using some form of an ObservableCollection or doing the event notification work yourself, you end up serializing and deserializing far too many times. This is also way too much code throughout your models to be useful.
Instead, I overrode TableEntity's WriteEntity and ReadEntity methods to write custom serialization and deserialization code that is only called when an entity is retrieved from the CloudTable or committed to it -- so only once for each retrieve, update operation etc.
Code below. I've illustrated a more complex example, where my TableEntity contains a class which in turn contains a dictionary.
public class MeetingLayoutEntity : TableEntity
{
/// <summary>
/// Extends TableEntity, the base class for entries in Azure CosmosDB Table tables.
/// </summary>
public MeetingLayoutEntity() { }
public MeetingLayoutEntity(MeetingLayout layout, string partition, string meetingId)
{
this.Layout = layout;
this.PartitionKey = partition;
this.RowKey = meetingId;
}
// Complex object which will be serialized/persisted as a JSON.
[IgnoreProperty]
public MeetingLayout Layout { get; set; }
public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
{
// This line will write partition key and row key, but not Layout since it has the IgnoreProperty attribute
var x = base.WriteEntity(operationContext);
// Writing x manually as a serialized string.
x[nameof(this.Layout)] = new EntityProperty(JsonConvert.SerializeObject(this.Layout));
return x;
}
public override void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
{
base.ReadEntity(properties, operationContext);
if (properties.ContainsKey(nameof(this.Layout)))
{
this.Layout = JsonConvert.DeserializeObject<MeetingLayout>(properties[nameof(this.Layout)].StringValue);
}
}
}
Learn more about ReadEntity and WriteEntity.