You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET AWS SDK中AsyncSearch类访问DynamoDB分页令牌的问题

使用AWS SDK for .NET的AsyncSearch实现分页:获取私有DocumentSearch的分页令牌

问题描述

我正在研究AWS SDK for .NET中的DynamoDB操作,想用AsyncSearch<T>类执行表查询并实现分页功能。根据SDK文档,分页令牌存储在底层的Search对象中,该对象通过AsyncSearch<T>的DocumentSearch属性访问,且会在调用GetNextSetAsync()后被设置。但问题是DocumentSearch是私有属性,无法直接访问。

我尝试包装AsyncSearch<T>来暴露分页令牌,但因DocumentSearch私有,代码无法编译:

public interface IAsyncSearch<T>
{
    bool IsDone { get; }
    string PaginationToken { get; }
    Dictionary<string, AttributeValue> NextKey { get; }

    Task<List<T>> GetRemainingAsync(CancellationToken token = default);
    Task<List<T>> GetNextSetAsync(CancellationToken token = default);
}

public class CustomAsyncSearch<T> : IAsyncSearch<T>
{
    private readonly AsyncSearch<T> _search;

    public CustomAsyncSearch(AsyncSearch<T> search)
    {
        _search = search;
    }

    async Task<List<T>> IAsyncSearch<T>.GetRemainingAsync(CancellationToken token)
    {
        return await _search.GetRemainingAsync(token);
    }

    async Task<List<T>> IAsyncSearch<T>.GetNextSetAsync(CancellationToken token)
    {
        return await _search.GetNextSetAsync(token);
    }

    public bool IsDone => _search.IsDone;
    public string PaginationToken => _search.DocumentSearch.PaginationToken?.ToString();
    
    public Dictionary<string, AttributeValue> NextKey => _search.DocumentSearch.NextKey;
}

附AWS SDK中AsyncSearch<T>的源码:

namespace Amazon.DynamoDBv2.DataModel;

public class AsyncSearch<T>
{
    private Search DocumentSearch { get; set; }
    private DynamoDBContext SourceContext { get; set; }
    private DynamoDBFlatConfig Config { get; set; }

    public bool IsDone => DocumentSearch.IsDone;

    internal AsyncSearch(DynamoDBContext source, DynamoDBContext.ContextSearch contextSearch)
    {
        SourceContext = source;
        DocumentSearch = contextSearch.Search;
        Config = contextSearch.FlatConfig;
    }

    public async Task<List<T>> GetNextSetAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        List<Document> documents = await DocumentSearch.GetNextSetHelperAsync(cancellationToken).ConfigureAwait(false);
        return SourceContext.FromDocumentsHelper<T>(documents, Config).ToList();
    }

    public async Task<List<T>> GetRemainingAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        List<Document> documents = await DocumentSearch.GetRemainingHelperAsync(cancellationToken).ConfigureAwait(false);
        return SourceContext.FromDocumentsHelper<T>(documents, Config).ToList();
    }
}

我需要在不修改AsyncSearch<T>内部结构的前提下,获取分页令牌,或者找到正确实现分页的方法。


可行解决方案

方案1:反射访问私有DocumentSearch属性

通过反射获取AsyncSearch<T>的私有DocumentSearch属性,进而提取分页令牌和NextKey。修改CustomAsyncSearch<T>类如下:

public class CustomAsyncSearch<T> : IAsyncSearch<T>
{
    private readonly AsyncSearch<T> _search;
    private readonly Search _documentSearch;

    public CustomAsyncSearch(AsyncSearch<T> search)
    {
        _search = search;
        // 反射获取私有DocumentSearch属性
        var documentSearchProperty = typeof(AsyncSearch<T>).GetProperty(
            "DocumentSearch", 
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        _documentSearch = documentSearchProperty?.GetValue(search) as Search;
    }

    public async Task<List<T>> GetRemainingAsync(CancellationToken token)
    {
        return await _search.GetRemainingAsync(token);
    }

    public async Task<List<T>> GetNextSetAsync(CancellationToken token)
    {
        var result = await _search.GetNextSetAsync(token);
        // 调用GetNextSetAsync后,DocumentSearch的分页令牌会自动更新
        return result;
    }

    public bool IsDone => _search.IsDone;
    public string PaginationToken => _documentSearch?.PaginationToken?.ToString();
    public Dictionary<string, AttributeValue> NextKey => _documentSearch?.NextKey;
}

注意:反射依赖SDK内部实现,若AWS后续修改AsyncSearch<T>的私有成员结构,代码会失效。

方案2:直接使用低级API

跳过AsyncSearch<T>的封装,直接用AmazonDynamoDBClient的QueryAsync/ScanAsync方法,这些方法返回的响应包含LastEvaluatedKey,可直接用于分页:

private readonly AmazonDynamoDBClient _dynamoDBClient;
private Dictionary<string, AttributeValue> _lastEvaluatedKey;

public async Task<List<T>> QueryWithPaginationAsync(string tableName, int pageSize = 10)
{
    var request = new QueryRequest
    {
        TableName = tableName,
        // 设置查询条件、ProjectionExpression等
        ExclusiveStartKey = _lastEvaluatedKey,
        Limit = pageSize
    };

    var response = await _dynamoDBClient.QueryAsync(request);
    // 自行实现AttributeValue到强类型T的转换逻辑
    var items = response.Items.Select(ConvertToT).ToList();
    
    // 保存下一页的起始键
    _lastEvaluatedKey = response.LastEvaluatedKey;
    return items;
}

private T ConvertToT(Dictionary<string, AttributeValue> item)
{
    // 实现对象转换逻辑
}

这种方法稳定性更高,不依赖SDK内部封装,但需要自行处理对象与AttributeValue的转换。

方案3:复用AsyncSearch实例作为分页状态

如果是内存内保持状态的场景(如单会话分页),可直接保存AsyncSearch<T>实例,它内部已维护分页状态,每次调用GetNextSetAsync()会自动加载下一页:

private AsyncSearch<MyItem> _currentSearch;
private readonly DynamoDBContext _context;

public async Task<List<MyItem>> GetFirstPageAsync()
{
    _currentSearch = _context.QueryAsync<MyItem>(/* 传入查询参数 */);
    return await _currentSearch.GetNextSetAsync();
}

public async Task<List<MyItem>> GetNextPageAsync()
{
    if (_currentSearch == null || _currentSearch.IsDone)
        return new List<MyItem>();
    
    return await _currentSearch.GetNextSetAsync();
}

这种方法最简单,但不适合跨请求保存分页状态的场景(如Web应用前后端分页)。


内容的提问来源于stack exchange,提问作者viking

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 18:20:07