Используйте yield для отсрочки вызова хранилища BLOB-объектов Azure - c # - PullRequest
0 голосов
/ 10 октября 2018

В настоящее время у меня есть метод, в котором я получаю список имен файлов BLOB-объектов из Azure.Метод следующий:

internal async Task<IEnumerable<BlobItem>> GetFiles(CloudBlobContainer container, string directoryName, bool recursive)
{
    var results = new List<BlobItem>();
    BlobContinuationToken continuationToken = null;

    do
    {
        var response = await container.GetDirectoryReference(directoryName).ListBlobsSegmentedAsync(false, BlobListingDetails.None, 100, continuationToken, null, null);

        continuationToken = response.ContinuationToken;
        foreach (var item in response.Results)
        {
            if (item.GetType() != typeof(CloudBlobDirectory))
                results.Add(new BlobItem(item));
            else if (recursive)
                results.AddRange(await GetFiles(container, ((CloudBlobDirectory)item).Prefix, recursive));
        }
    }
    while (continuationToken != null);

    return results;
}

Что мне не нравится в приведенном выше коде, так это то, что я перебираю все файлы и добавляю в результаты, пока токен отмены не станет нулевым.В общем, иди и получи все, затем остановись и вернись.

Я не думаю, что это слишком эффективно - я думал, что мог бы дать результаты, так что он только идет и получает следующую «партию» результатовкогда я готов к этому (из вызывающего кода).

Я не очень знаком с использованием yield и придумал это, но я думаю, что это может не откладывать вызов ListBlobSegment.Вот мой код:

internal IEnumerable<BlobItem> GetFiles(CloudBlobContainer container, string directoryName, bool recursive)
{
    var results = new List<BlobItem>();
    BlobContinuationToken continuationToken = null;

    do
    {
        var response = container.GetDirectoryReference(directoryName).ListBlobsSegmentedAsync(false, BlobListingDetails.None, 100, continuationToken, null, null).GetAwaiter().GetResult();

        continuationToken = response.ContinuationToken;
        foreach (var item in response.Results)
        {


            if (item.GetType() != typeof(CloudBlobDirectory))
                yield return new BlobItem(item);
            else if (recursive)
            {
                var internalResponse =  GetFiles(container, ((CloudBlobDirectory)item).Prefix, recursive));
                foreach (var intItem in internalResponse)
                {
                    yield return intItem;
                }
            }
        }
    }
    while (continuationToken != null);
}

Может кто-нибудь посоветовать мне, если я правильно использую оператор yield?Как уже упоминалось, никогда раньше не использовал это в гневе и хочу сделать это правильно :-) Моя цель, мы надеемся, отложить вызов службы и сделать код более эффективным для вызова.

Заранее спасибо за любые указатели!

ПРИМЕЧАНИЕ: использование этих API для хранения больших двоичных объектов

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;

1 Ответ

0 голосов
/ 10 октября 2018

Изменить 2018-11-15 Начиная с C # 8 , вы сможете использовать IAsyncEnumerable:

async IAsyncEnumerable<int> GetBigResultsAsync()
{
    await foreach (var result in GetResultsAsync())
    {
        if (result > 20) yield return result; 
    }
}

IAsyncEnumerable<string> GetAsyncAnswers()
{
    return AsyncEnum.Enumerate<string>(async consumer =>
    {
        foreach (var question in GetQuestions())
        {
            string theAnswer = await answeringService.GetAnswer(question);
            await consumer.YieldAsync(theAnswer);
        }
    });
}

https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/

https://archive.codeplex.com/?p=asyncenum

Оригинальный ответ

Вам нужно использовать IObservable<BlobItem> вместо IEnumerable<BlobItem> в этом случае.По крайней мере, внутренне, в зависимости от того, как вы на самом деле звоните GetFiles.

. Этот вопрос имеет несколько замечательных объяснений, на которые вам следует обратить внимание:

Как получить возвращаемый элемент при выполнении Задачи.WhenAny

или соответствующее сообщение в блоге для принятого ответа.

Примечание: возможно, вы захотите использовать параметр useFlatBlobListing=true для ListBlobsSegmentedAsync вместоручного выполнения рекурсивного кода.

Некоторый быстрый код, который описывает, как это может выглядеть (не проверено или что-то еще)

public IEnumerable<BlobItem> GetFilesAsEnumerable(CloudBlobContainer container, string directoryName, bool recursive)
{
    return GetFiles(container, directoryName, recursive).ToEnumerable();
}

public IObservable<BlobItem> GetFiles(CloudBlobContainer container, string directoryName, bool recursive)
{
    return Observable.Create<BlobItem>(async obs =>
        {
            BlobContinuationToken continuationToken = null;

            do
            {
                var response = await container.GetDirectoryReference(directoryName).ListBlobsSegmentedAsync(/*useFlatBlobListing*/ recursive, BlobListingDetails.None, 100, continuationToken, null, null);

                continuationToken = response.ContinuationToken;
                foreach (var item in response.Results)
                {
                    // Only required if recursive == false
                    if (item.GetType() != typeof(CloudBlobDirectory))
                        obs.OnNext(new BlobItem(item));
                }
            }
            while (continuationToken != null);
        });
}
...