如何异步执行 Azure 表存储查询?客户端版本 4.0.1

Posted

技术标签:

【中文标题】如何异步执行 Azure 表存储查询?客户端版本 4.0.1【英文标题】:How to execute an Azure table storage query async? client version 4.0.1 【发布时间】:2014-08-05 17:26:33 【问题描述】:

想要在 Azure 存储客户端版本 4.0.1 上执行异步查询

没有方法 ExecuteQueryAsync()..

我错过了什么?我们还应该继续使用 ExecuteQuerySegmentedAsync 吗? 谢谢。

【问题讨论】:

谁能指点我一个文档,讨论存储 API 中哪些方法可用,哪些方法不在不同的架构上?令人沮丧的是文档和入门指南说要做一件事,然后方法不存在。 【参考方案1】:

我最终制作了一个扩展方法来使用 ExecuteQuerySegmentedAsync。 我不确定这个解决方案是否是最优的,如果有人有任何意见,请不要犹豫。

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;
    

【讨论】:

Joe,是的,您的代码示例是执行此操作的最佳方法(使用 ExecuteQuerySegmentedAsync 并结合每个段的结果)。你的代码看起来不错。我的唯一建议是,如果可能的话,通过使用接受整数的构造函数,将items 列表预分配到一个足够大的大小以在大多数情况下容纳所有实体。这将减少所需的调整大小。 @MikeFisher 感谢您的评论,会考虑。但是,很难先验地知道查询的结果编号。也许它可以在每次迭代时进行优化。您将列表的大小调整为大 1000 条记录。 你忘记将cancellationToken传入ExecuteQuerySegmentedAsync 每当我调用 'TableQuerySegment seg = await table.ExecuteQuerySegmentedAsync(query, token);'我的应用程序似乎挂起。我在 UWP 项目中这样做。 为什么没有 cancelToken 传递给 ExecuteQuerySegmentedAsync?可能是因为您不希望此方法抛出 OperationCancelledException?你想让它返回你已经收到的结果吗?抛出异常并将 CancellationToken 传递给 ExecuteQuerySegmentedAsync() 方法以立即停止请求不是更好吗?否则,上述扩展方法的调用代码可能会错误地获取结果,认为这些都是项目,而操作被取消,这些只是其中的一部分。【参考方案2】:

当表查询包含 take 子句时,指定的解决方案将返回比查询请求更多的项目。 while 表达式的微小变化将解决该问题。

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;

已编辑:感谢 PaulG 的建议,更正了当查询包含 take 子句并且 ExecuteQuerySegmentedAsync 多次返回项目时结果计数的问题。

【讨论】:

【参考方案3】:

这是对@JoseCh. 的回答的补充。

这是一个扩展方法,允许您指定 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;
     

如果你只想返回存储中单个列的结果集,可以使用它:

// 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()

【讨论】:

以上是关于如何异步执行 Azure 表存储查询?客户端版本 4.0.1的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Azure 表存储中使用 partitionkey 加快查询速度

AZURE 表存储、ODATA 和更友好的 URI 查询

从 Azure 表存储中获取 1000 多个数据集

如何从 azure 表存储中编写组合查询?

Azure 表存储 - 查询时间戳

无法在 Sql Azure 上删除表