Почему мой TakeLimit не удостоен чести TableQuery?

Я хотел бы получить первые n строк из моей таблицы Azure с помощью простого TableQuery. Но с кодом ниже все строки выбираются независимо от моего лимита с Take.

Что я делаю неправильно?

int entryLimit = 5;

var table = GetFromHelperFunc();

TableQuery<MyEntity> query = new TableQuery<MyEntity>()
    .Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "MyPK"))
    .Take(entryLimit);

List<FeedEntry> entryList = new List<FeedEntry>();
TableQuerySegment<FeedEntry> currentSegment = null;

while (currentSegment == null || currentSegment.ContinuationToken != null)
{
    currentSegment = table.ExecuteQuerySegmented(query, this.EntryResolver, currentSegment != null ? currentSegment.ContinuationToken : null);
    entryList.AddRange(currentSegment.Results);
}


Trace.WriteLine(entryList.Count) // <-- Why does this exceed my limit?

2 ответа

Решение

Метод Take в SDK хранилища не работает, как в LINQ. Представьте, что вы делаете что-то вроде этого:

TableQuery<TableEntity> query = new TableQuery<TableEntity>()
                .Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "temp"))
                .Take(5);
var result = table.ExecuteQuery(query);

Когда вы начнете перебирать result изначально вы получите только 5 предметов. Но внизу, если вы продолжаете перебирать result, SDK будет продолжать запрашивать таблицу (и переходить к следующей "странице" из 5 пунктов).

Если в моей таблице 5000 элементов, этот код выведет все 5000 элементов (а под SDK будет выполнено 1000 запросов и получено 5 элементов на запрос):

TableQuery<TableEntity> query = new TableQuery<TableEntity>()
                .Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "temp"))
                .Take(5);
var result = table.ExecuteQuery(query);
foreach (var item in result)
{
    Trace.WriteLine(item.RowKey);
}

Следующий код получит ровно 5 элементов в 1 запросе и остановится на этом:

TableQuery<TableEntity> query = new TableQuery<TableEntity>()
                .Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "temp"))
                .Take(5);
var result = table.ExecuteQuery(query);
int index = 0;
foreach (var item in result)
{
    Console.WriteLine(item.RowKey);
    index++;
    if (index == 5)
        break;
}

На самом деле, метод Take() устанавливает размер страницы или "счет" (свойство TakeCount в TableQuery). Но вы все равно должны прекратить повторение по времени, если вы хотите только 5 записей.

В вашем примере вы должны изменить цикл while, чтобы он останавливался при достижении TakeCount (который вы установили, вызывая Take):

while (entryList.Count < query.TakeCount && (currentSegment == null || currentSegment.ContinuationToken != null))
{
    currentSegment = table.ExecuteQuerySegmented(query, currentSegment != null ? currentSegment.ContinuationToken : null);
    entryList.AddRange(currentSegment.Results);
}

В AFAIK Storage Client Library 2.0 была ошибка в реализации Take. Это было исправлено в версии 2.0.4.
Прочитайте последние комментарии на http://blogs.msdn.com/b/windowsazurestorage/archive/2012/11/06/windows-azure-storage-client-library-2-0-tables-deep-dive.aspx

Другие вопросы по тегам