Search code examples
c#.netazureazure-functionsazure-table-storage

Query with custom object type fails to convert custom fields in Azure Table Storage


I'm trying to query an Azure Table Storage instance from an Azure Function to retrieve either a single or multiple entities according to particular conditions (filters).

According to Microsoft's documentation of the Azure.Data.Tables SDK querying (I'm using v12.8.0) this should be possible by substituting the TableEntity type with your custom strongly-typed object when calling Query. Their snippets example its capability as below

// Define a strongly typed entity by implementing the ITableEntity interface.
public class OfficeSupplyEntity : ITableEntity
{
    public string Product { get; set; }
    public double Price { get; set; }
    public int Quantity { get; set; }
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public DateTimeOffset? Timestamp { get; set; }
    public ETag ETag { get; set; }
}

...
// Given this model class definition, here is how you'd write a query
double priceCutOff = 6.00;
Pageable<OfficeSupplyEntity> queryResultsLINQ = tableClient.Query<OfficeSupplyEntity>(ent => ent.Price >= priceCutOff);

Following this, I've a model defined as such

public class MyModel : ITableEntity
{
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public DateTimeOffset? Timestamp { get; set; }
    public ETag ETag { get; set; }

    public int PropertyA { get; set; }
    public string? PropertyB { get; set; }
    public Guid? PropertyC { get; set; }
    public Version PropertyD { get; set; }
    public Version PropertyE { get; set; }
    public Guid PropertyF { get; set; }
}

When I call to retrieve a single entity, as below, the entity properties are populated and the custom properties are visible in the objects Value Results View.

Response<TableEntity> results = _tableClient.GetEntity<TableEntity>(partitionKey, recordKey);

However, if I try to retrieve it using the custom model as shown below (following the docs example), the entity properties are populated by all customer properties are their default values.

Response<MyModel> results = _tableClient.GetEntity<MyModel>(partitionKey, recordKey);

The same is true when trying to use Query<MyModel> with a filter upon the Guid property PropertyC (as shown below), the Pageable<MyModel> result is empty. Presumably as it's unable to convert the TableEntity to MyModel?

_tableClient.Query<MyModel>(e => e.PropertyC == PropertyC)

Does anyone know why querying with a custom model doesn't appear to initialise correctly, as expected in the documentation? I've considered creating a custom converter, but performance is key with this function so therefore minimal custom-code overhead is needed.

Update

For context, these are the field definitions in the Table Storage

  • PartitionKey: String
  • RowKey : String
  • Timestamp : DateTimeOffset
  • PropertyA : Int32
  • PropertyB : String
  • PropertyC : Guid
  • PropertyD : String
  • PropertyE : String
  • PropertyF : Guid

In a record, either PropertyC or PropertyD might have a value, but not both or neither. And PropertyF will have the RowKey value of another entity record in a different partition.


Solution

  • As it turns out, casing is important. The properties were defined as propertyA in Table Storage, but PropertyA on the model, which meant they couldn't be resolved.

    Updating the custom property names to capitalised case in Table Storage led to it correctly parsing the custom object when using GetEntity<T> and Query<T>.