具有复杂 属性 的 Azure 表存储实体

Azure TableStorage entity with a Complex property

我正在尝试将 HashSet<string> 放入 Azure Table 存储中。我想为一个包含成员的项目建模:

public class DbProject : ITableEntity {
        public string Name { get; set; } = default!;
        public string Owner { get; set; } = default!;
        public HashSet<string> Members { get; set; } = default!;
        public DateTime CreatedOn { get; set; } = default!;

        public string PartitionKey { get; set; } = default!;
        public string RowKey { get;set; } = default!;
        public DateTimeOffset? Timestamp { get;set; } = default!;
        public ETag ETag { get;set; } = default!;
    }

'insert logic' 看起来像:

var dbProject = new DbProject {
    Name = projectName,
    Owner = owner,
    CreatedOn = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc),
    Members = new HashSet<string> { owner },
    PartitionKey = $"{owner}__{projectName}",
    RowKey = $"{owner}__{projectName}"
};

try {
    this.tableClient.AddEntity<DbProject>(dbProject);
}
catch (Exception ex) {
    Console.WriteLine(ex.Message);
}

我得到的错误是:

ErrorCode: InvalidInput

Content:
{"odata.error":{"code":"InvalidInput","message":{"lang":"en-US","value":"An error occurred while processing this request.\nRequestId:6019edb7-c002-001c-1be9-bd1379000000\nTime:2021-10-10T15:15:24.5069601Z"}}}

如果我删除 HashSet,它就像一个魅力,所以我猜想在 Azure Table 存储中创建记录时使用复杂类型有问题。

我想 运行 简单查询,例如:

public bool IsMember(string owner, string projectName) {
    var query = TableClient.CreateQueryFilter<DbProject>(u => u.Name == projectName && u.Members.Contains(owner));
    return tableClient.Query<DbProject>(query).Any();
}

您 运行 陷入此问题的原因是因为 Azure Table 存储不支持复杂数据类型(HashSet 是其中之一)。

有关受支持数据类型的列表,请参阅此 link:https://docs.microsoft.com/en-us/rest/api/storageservices/understanding-the-table-service-data-model#property-types

您需要做的是以某种方式将复杂数据类型序列化为一种受支持的数据类型(string 数据类型最合适)并保存。当您读取数据时,您将不得不将其反序列化。请注意,这样做您将失去查询此特定属性的能力。

由于您在评论中要求替代解决方案:最明显的选择是使用 CosmosDB 而不是 Table 存储。 Table 在许多情况下,存储空间极其有限。如果出于特定原因不需要使用 table 存储,建议将 CosmosDB 与 SQL API 一起用于新项目。

如 Jeremy 的评论中所述,您可以在那里很好地使用 HashSet:How to search on Cosmos DB in a complex JSON Object

如果您想保留 Table 存储并且不需要对您的 HashSet 进行查询:您可以将 HashSet 序列化为字符串,如 Gaurav 提到的那样。也许是这样的:

[IgnoreProperty]
public HashSet<string> Members { get; set; }

public override void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
{
    Members = JsonConvert.DeserializeObject<HashSet<string>>(properties[nameof(Members)].StringValue);
}

public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
{
    var properties = new Dictionary<string, EntityProperty>();
    properties.Add(nameof(Members), new EntityProperty(JsonConvert.SerializeObject(Members)));
    return properties;
}

你也可以看看这个:Adding Complex Properties of a TableEntity to Azure Table Storage