Joey Schluchter
Joey Schluchter

Reputation: 2612

TableQuery<T> from Azure TableStorage that filters on PartitionKey

I'm trying to abstract geting all entities from a Table by partitionKey, like so:

public List<T> GetEntities<T>(string partitionKey, T entity) where T : TableEntity
    {
        try
        {
            var tableClient = _account.CreateCloudTableClient();
            var table = tableClient.GetTableReference(entity.GetType().Name.ToLower());
            var exQuery =
                new TableQuery<T>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal,
                                                                             partitionKey));

            var results = table.ExecuteQuery(exQuery).Select(ent => (T) ent).ToList();
            return results;
        }
        catch (StorageException ex)
        {
            //TODO: Add more trace info
            Trace.TraceInformation("Unable to retrieve entity based on query specs");
            return null;
        }
    }

However, It's failing on the

new TableQuery<T>

because the TElement does not have a parameterless constructor.

Upvotes: 19

Views: 19825

Answers (3)

Rahul Garg
Rahul Garg

Reputation: 4329

I wrote a small generic repository for table storage :

 public class CloudTableRepository<T> where T : ITableEntity,new ()
{
    private readonly string _tableName;
    private CloudTable _table;

    public CloudTableRepository(string tableName)
    {
        _tableName = tableName;
        InitializeTable();
    }

    #region Public Methods

    public virtual async Task<List<T>> GetPartitionAsync(string partitionKey, int takeCount = 1000)
    {
        var result = new List<T>();
        var query =
            new TableQuery<T>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal,
                partitionKey));
        query.TakeCount = takeCount;
        TableContinuationToken tableContinuationToken = null;
        do
        {
            var queryResponse = await _table.ExecuteQuerySegmentedAsync(query, tableContinuationToken);
            tableContinuationToken = queryResponse.ContinuationToken;
            result.AddRange(queryResponse.Results);
        } while (tableContinuationToken != null);
        return result;
    }

    public virtual async Task<TableResult> GetSingleAsync(string partitionKey, string rowKey)
    {
       return  await GetSingle(partitionKey, rowKey);
    }

    public virtual async Task<T> UpdateAsync(T tableEntityData)
    {
        var updateCallistConfig = await GetSingleAsync(tableEntityData.PartitionKey, tableEntityData.RowKey);
        if (updateCallistConfig != null)
        {
            var updateOperation = TableOperation.InsertOrMerge(tableEntityData);
            var tableResult = await _table.ExecuteAsync(updateOperation);
            return (T) tableResult.Result;
        }
        return default(T);
    }

    public virtual async Task<T> AddAsync(T tableEntityData)
    {
        var retrieveOperation = TableOperation.Insert(tableEntityData);
        var tableResult = await _table.ExecuteAsync(retrieveOperation);
        return (T) tableResult.Result;
    }


    #endregion

    #region Private Methods

    private async void InitializeTable()
    {
        var storageAccount =
            CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("TableStorageConnectionString"));
        var tableClient = storageAccount.CreateCloudTableClient();
        _table = tableClient.GetTableReference(_tableName);
        await _table.CreateIfNotExistsAsync();
    }

    private async Task<TableResult> GetSingle(string partitionKey, string rowKey)
    {
        var retrieveOperation = TableOperation.Retrieve<T>(partitionKey, rowKey);
        var tableResult = await _table.ExecuteAsync(retrieveOperation);
        return tableResult; //(T) tableResult.Result;
    }

    #endregion
}

Upvotes: 8

Thomas
Thomas

Reputation: 29491

In addition to @serdar-ozler-microsoft answer, you don't even need to convert and cast the entities to return.

The CloudTable.ExecuteQuery method has an overload that accept a generic type:

public IEnumerable<TElement> ExecuteQuery<TElement>(
TableQuery<TElement> query,
TableRequestOptions requestOptions = null,
OperationContext operationContext = null)
where TElement : new(), ITableEntity

You can also use Linq to filter on the Table Service.

So that you can rewrite your method like that :

public List<T> GetEntities<T>(string partitionKey, T entity) where T : ITableEntity, new()
{
    try
    {
        var tableClient = _account.CreateCloudTableClient();
        var table = tableClient.GetTableReference(entity.GetType().Name.ToLower());
        return table.CreateQuery<T>().Where(e => e.PartitionKey == partitionKey).ToList();
    }
    catch (StorageException ex)
    {
        //TODO: Add more trace info
        Trace.TraceInformation("Unable to retrieve entity based on query specs");
        return null;
    }
}

Upvotes: 3

Serdar Ozler
Serdar Ozler

Reputation: 3802

As you also mentioned in your question, T must have a parameterless constructor. Hence, please change the definition of your method as follows:

public List<T> GetEntities<T>(string partitionKey, T entity) where T : TableEntity, new ()

Upvotes: 28

Related Questions