我的回答扩展了肇兴将复杂实体属性写入 JSON 并将其持久化到 Azure CosmosDB 的方法。
但是,setter 中的字符串和对象之间的序列化会导致以下问题:
- 例如,如果您要从字典 DicProperty 中添加或删除项目,则不会调用其设置器,因为您没有修改字典但修改了其内容。同样,在您对序列化自定义对象或类感兴趣的更复杂用例中,修改类的成员不会触发 setter。当实体提交到 CloudTable 时,这可能会导致数据丢失。
- 如果您确实选择在复杂属性上实现 INotifyPropertyChanged 之类的东西,或者通过使用某种形式的 ObservableCollection 或自己执行事件通知,您最终会序列化和反序列化太多次。这在整个模型中也有太多的代码没有用。
相反,我重写了 TableEntity 的 WriteEntity 和 ReadEntity 方法来编写自定义序列化和反序列化代码,这些代码仅在从 CloudTable 检索实体或提交给它时调用——因此每次检索、更新操作等只调用一次。
代码如下。我已经说明了一个更复杂的示例,其中我的 TableEntity 包含一个类,而该类又包含一个字典。
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);
}
}
}
了解有关ReadEntity和WriteEntity的更多信息。