I'm trying to use the LINQ IEnumerable.Aggregate function to create a string consisting of files retrieved through async calls. Not a hundred percent sure that it's possible, and I'm also aware that there are other solutions, but I'd like to give it a try.
For now my code looks like this:
private static async Task<string> GetFiles(IEnumerable<string> filePaths)
{
    return filePaths.Aggregate(async (current, path) => current + await GetFile(path));
}
But the "async" inside the method call is error marked saying "the return of an async method must be void, Task, or Task". I get that error in general, but I'm not sure how to arrange this specific case to avoid it. Any ideas?
UPDATE:
Just to clarify, the GetFile() method is indeed asynchronous and returns Task<string>:
private static async Task<string> GetFile(string filePath) { ... }
No need to get into the specific code, but for those interested it uses HttpClient.GetAsync(filePath) and the returns its response.Content.ReadAsStringAsync().Result.
Aggregate method won't work asynchronously. It doesn't support Task based delegates. You need to create a result sequence yourself by awaiting it in prior to call Aggregate method. 
Something like this should work:
private static async Task<string> GetFiles(IEnumerable<string> filePaths)
{
    var files = filePaths
        .Select(p => GetFile(p))
        .ToArray();
    var results = await Task.WhenAll(files);
    return results
        .Aggregate((current, path) => current + path);
}
                        As @Sriram said, LINQ and async-await don't work that well together because there's no built-in support for async Task delegates.
What you can do is create an async overload of aggregate yourself:
public static class AsynchronousEnumerable
{
    public static async Task<TSource> AggregateAsync<TSource>
                                      (this IEnumerable<TSource> source,
                                       Func<TSource, TSource, Task<TSource>> func)
    {
       using (IEnumerator<TSource> e = source.GetEnumerator())
       {
            if (!e.MoveNext())
            {
                throw new InvalidOperationException("Sequence contains no elements");
            }
            TSource result = e.Current;
            while (e.MoveNext()) result = await func(result, e.Current);
            return result;
        }
    }
}
And now you can do the following:
private static Task<string> GetFiles(IEnumerable<string> filePaths)
{
    return filePaths.AggregateAsync(async (current, path) => current + 
                                                             await GetFile(path));
}
                        If you want to use async inside Aggregate, you must realize that anything asynchronous always returns a Task. Taking this into account, it becomes obvious that the result of your Aggregate call should therefore also be a Task.
For example, calculating the sum of a collection of numbers that are returned asynchronously:
private static async Task<int> GetSumAsync(IEnumerable<Task<int>> numbers) {
  return await numbers
    .Aggregate(Task.FromResult(0), async (sumSoFar, nextNumber) => (await sumSoFar) + (await nextNumber));
}
I am a little confused as to what you're exactly hoping to do with your GetFiles method. You do realize that Aggregate reduces a collection to just one thing, right? (The 'sum' function is a good example)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With