7

我在 Azure 表中存储了一些这样的枚举

pk   rk |    en     fr     de   ...

foo  1  |  'Eune' 'Fune' 'Dune' ...
foo  2  |  'Edoe' 'Fdoe' 'Ddoe' ...

bar  1  |  'Unee' 'Unef' 'Trid' ...
bar  2  |  'Diee' 'Dief' 'Died' ...
bar  3  |  'Trie' 'Tref' 'Trid' ...

en, , fr, deetc... 是语言代码,分别是表中的列名。

我应该创建什么样的TableEntity才能正确加载它

public class FooEntity : TableEntity
{
    public Dictionary<string, string> Descriptions {get; set} // ?
}

然后像myFoo["fr"]...一样使用它们有可能吗?

假设我有英文 GUI,我需要显示一个Foo带有Eune/Edoe作为选择值的选择。

4

4 回答 4

13

我的回答扩展了肇兴将复杂实体属性写入 JSON 并将其持久化到 Azure CosmosDB 的方法。

但是,setter 中的字符串和对象之间的序列化会导致以下问题:

  1. 例如,如果您要从字典 DicProperty 中添加或删除项目,则不会调用其设置器,因为您没有修改字典但修改了其内容。同样,在您对序列化自定义对象或类感兴趣的更复杂用例中,修改类的成员不会触发 setter。当实体提交到 CloudTable 时,这可能会导致数据丢失。
    1. 如果您确实选择在复杂属性上实现 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);
        }
    }

}

了解有关ReadEntityWriteEntity的更多信息。

于 2018-06-10T07:07:22.163 回答
12

Azure 存储表不支持将数组、列表或字典作为实体属性。您可以在此处找到所有受支持的属性类型(“属性类型”部分)。

但是,您可以考虑将数组/列表/字典序列化为字符串属性,并在 TableEntity 类中声明具有 [IgnoreProperty] 属性的属性,以将序列化的字符串转换回数组/列表/字典。

public class MyEntity : TableEntity
{
    public string DicPropertyRaw { get; set; }

    [IgnoreProperty]
    public Dictionary<string, string> DicProperty
    {
        get
        {
            return Deserialize(DicPropertyRaw);
        }

        set
        {
            DicPropertyRaw = Serialize(value);
        }
    }
}
于 2017-08-07T01:41:01.430 回答
1

您可以使用 ObjectFlatenerRecomposer api Nuget 包将任何类型的对象写入表存储: https ://www.nuget.org/packages/ObjectFlatenerRecomposer/ 2.0.0 版还支持数组和可枚举。这些属性将在写入表存储之前透明地转换为 json 字符串,并在从表存储中读取时反序列化为原始对象。该 api 还允许您将复杂的对象写入表存储。

于 2018-12-15T19:25:55.477 回答
0

使用自定义属性读取/写入您的复杂类型

该解决方案具有以下优点:

  1. 您的实体对象最终只有一个复杂类型的属性(而不是使用 时的两个[IgnoreProperty])。
  2. 如果将 ReadEntity 和 WriteEntity 代码移至基类,则所有序列化代码都可以从您的实体中抽象出来。

此解决方案在此处的网络上有详细说明: https ://www.devprotocol.com/azure-table-storage-and-complex-types-stored-in-json/

Github 仓库: https ://github.com/jtourlamin/DevProtocol.Azure.EntityPropertyConverter

如果您更喜欢使用 LINQ 的解决方案: https ://blog.bitscry.com/2019/04/12/adding-complex-properties-of-a-tableentity-to-azure-table-storage/

更好的是,使用最新的库以正确的方式进行操作,并使用内置的 Flatten 和 ConvertBack:https ://stackoverflow.com/a/35068938/1785972

于 2020-05-22T12:21:30.470 回答