How to execute an Azure table storage query async? client version 4.0.1
Asked Answered
F

3

69

Want to execute queries Async on Azure Storage Client Version 4.0.1

There is NO method ExecuteQueryAsync()..

I am missing something? Should we continue to use the ExecuteQuerySegmentedAsync still? Thanks.

Fishy answered 15/6, 2014 at 21:38 Comment(1)
Can anyone point me to a doc that talks about which methods are available in the storage API and which aren't on different architectures? It's frustrating that the docs and getting started guides say to do one thing and then the methods aren't there.Polychrome
F
95

I end up making an extension method to use ExecuteQuerySegmentedAsync. I am not sure whether this solution is optimal, if anybody has any comment please don’t hesitate.

public static async Task<IList<T>> ExecuteQueryAsync<T>(this CloudTable table, TableQuery<T> query, CancellationToken ct = default(CancellationToken), Action<IList<T>> onProgress = null) where T : ITableEntity, new()
    {

        var items = new List<T>();
        TableContinuationToken token = null;

        do
        {

            TableQuerySegment<T> seg = await table.ExecuteQuerySegmentedAsync<T>(query, token);
            token = seg.ContinuationToken;
            items.AddRange(seg);
            if (onProgress != null) onProgress(items);

        } while (token != null && !ct.IsCancellationRequested);

        return items;
    }
Fishy answered 17/6, 2014 at 17:56 Comment(6)
Joe, yes, your code example is the best way to do this (using ExecuteQuerySegmentedAsync and combining the results from each segment). Your code looks good. The only suggestion I have is for you to, if possible, pre-allocate the items list to a size that will be big enough to hold all the entities most of the time by using the constructor that accepts an integer. This will reduce the amount of resizing needed.Nephritis
@MikeFisher Thanks for your comment, will consider. However, it’s hard to know a priori the results number from a query. Perhaps it can be optimized on each iteration. You resize to a list 1000 records larger.Fishy
You forgot to pass in the cancellationToken into ExecuteQuerySegmentedAsyncSenility
Whenever I call 'TableQuerySegment<T> seg = await table.ExecuteQuerySegmentedAsync<T>(query, token);' my app seems to hang. I'm doing this in a UWP project.Judi
Why no cancellationToken is passed to ExecuteQuerySegmentedAsync? Maybe because you don't want this method to throw an OperationCancelledException? Do you want it to return the results you've already received? Wouldn't it be better to throw an exception instead and to pass the CancellationToken into ExecuteQuerySegmentedAsync() method to stop the request immediately? Otherwise, the calling code of the above extension method may wrongly take the results, thinking these are all the items, whereas the operation was cancelled and these are just some of them.Incommunicable
@Artemious even if all your statements are correct it doesn't make sense to not pass the ct (CancellationToken) into the ExecuteQuerySegmentedAsync since this extensions doesn't use it anywhere else. I think @Jose Ch. forgot to add it, I tried to edit the post because ct is lower than 6 characters :<Phionna
N
15

When table query contains take clause specified solution will return more items than requested by query. Small change of while expression will solve that problem.

public static async Task<IList<T>> ExecuteQueryAsync<T>(this CloudTable table, TableQuery<T> query, CancellationToken ct = default(CancellationToken), Action<IList<T>> onProgress = null) where T : ITableEntity, new()
{
    var runningQuery = new TableQuery<T>()
    {
        FilterString = query.FilterString,
        SelectColumns = query.SelectColumns
    };

    var items = new List<T>();
    TableContinuationToken token = null;

    do
    {
        runningQuery.TakeCount = query.TakeCount - items.Count;

        TableQuerySegment<T> seg = await table.ExecuteQuerySegmentedAsync<T>(runningQuery, token);
        token = seg.ContinuationToken;
        items.AddRange(seg);
        if (onProgress != null) onProgress(items);

    } while (token != null && !ct.IsCancellationRequested && (query.TakeCount == null || items.Count < query.TakeCount.Value));

    return items;
}

EDITED: Thanks to a suggestion from PaulG, corrected the issue with result count when query contains take clause and ExecuteQuerySegmentedAsync returns items in several passes.

Negate answered 3/1, 2015 at 19:36 Comment(0)
N
1

This is in addition on to @JoseCh.'s answer.

Here is an extension method which allows you to specify an EntityResolver:

public static async Task<IList<TResult>> ExecuteQueryAsync<T, TResult>(this CloudTable table, TableQuery query, EntityResolver<TResult> resolver, Action<IList<TResult>> onProgress = null, CancellationToken cancelToken = default(CancellationToken))
            where T : ITableEntity, new()
{
    var items = new List<TResult>();
    TableContinuationToken token = null;

    do
    {
        TableQuerySegment<TResult> seg = await table.ExecuteQuerySegmentedAsync(query: query, resolver: resolver, token: new TableContinuationToken(), cancellationToken: cancelToken).ConfigureAwait(false);
        token = seg.ContinuationToken;
        items.AddRange(seg);
        onProgress?.Invoke(items);
     }
     while (token != null && !cancelToken.IsCancellationRequested);
         return items;
     }
}

It can be used if you only want to return the result set of a single column in storage:

// maps to a column name in storage
string propertyName = nameof(example.Category);

// Define the query, and select only the Category property.
var projectionQuery = new TableQuery().Select(new string[] { propertyName });

// Define an entity resolver to work with the entity after retrieval.
EntityResolver<string> resolver = (pk, rk, ts, props, etag) => props.ContainsKey(propertyName) ? props[propertyName].StringValue : null;

var categories = (await someTable.ExecuteQueryAsync<DynamicTableEntity, string>(query: projectionQuery, resolver: resolver).ConfigureAwait(false)).ToList()
Noctilucent answered 25/3, 2019 at 0:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.