15

I'm trying to copy a list of files to a directory. I'm using async / await. But I've been getting this compilation error

The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.

This is what my code looks like

async Task<int> CopyFilesToFolder(List<string> fileList, 
            IProgress<int> progress, CancellationToken ct)
{
    int totalCount = fileList.Count;
    int processCount = await Task.Run<int>(() =>
    {
        int tempCount = 0;
        foreach (var file in fileList)
        {
            string outputFile = Path.Combine(outputPath, file);

            await CopyFileAsync(file, outputFile); //<-- ERROR: Compilation Error 

            ct.ThrowIfCancellationRequested();
            tempCount++;
            if (progress != null)
            {
                progress.Report((tempCount * 100 / totalCount)));
            }

        }

        return tempCount;
    });
    return processCount;
}


private async Task CopyFileAsync(string sourcePath, string destinationPath)
{
    using (Stream source = File.Open(sourcePath, FileMode.Open))
    {
        using (Stream destination = File.Create(destinationPath))
        {
            await source.CopyToAsync(destination);
        }
    }

}

Pls can anyone point out what am I missing here ?

abhilash
  • 5,605
  • 3
  • 36
  • 59
  • 1
    You haven't marked the lambda with the `async` keyword? Basically, if you extracted the lambda inside `Task.Run` it would not be an `async` method, so you couldn't await the result. – Daniel Kelley Sep 10 '14 at 14:38
  • `await Task.Run(() => ...` -- the lambda is not `async`. – Jon Sep 10 '14 at 14:39
  • 1
    This should be written without a lambda or `Task.Run`, because it's only IO-bound work. See http://pastebin.com/p83gkkTk for an example solution (I'd post this as an answer, but it's already closed). – Tim S. Sep 10 '14 at 14:50
  • @TimS.Thanks for that, Yes I Agree. – abhilash Sep 10 '14 at 15:00

1 Answers1

34
int processCount = await Task.Run<int>(() =>

Should be

int processCount = await Task.Run<int>(async () =>

Remember that a lambda is just shorthand for defining a method. So, your outer method is async, but in this case you're trying to use await within a lambda (which is a different method than your outer method). So your lambda must be marked async as well.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 1
    Something about `await Task.Run(async () =>` seems redundant to me. Is there a better way to write this? – Tim S. Sep 10 '14 at 14:40
  • 1
    @TimS.: No; each part is important and means something different. `async () =>` makes the *lambda* `async`, `Task.Run` runs that lambda on a thread pool thread, and `await` means that the calling method is (asynchronously) waiting for that lambda to complete. – Stephen Cleary Sep 10 '14 at 14:42
  • 2
    Sorry, I was unclear: `Task.Run` seems to usually be used to offload something that would, otherwise, be synchronous, CPU-bound work. This doesn't appear to have any significant CPU work, just IO-bound work. So wouldn't it be possible, and better, to write it in a way that doesn't use `Task.Run`, but simply `await`s an asynchronous method? – Tim S. Sep 10 '14 at 14:44
  • 2
    Ah, I see. Yes, using `Task.Run` for I/O work is superfluous, and that does appear to be the case here. – Stephen Cleary Sep 10 '14 at 14:46
  • 1
    This is what I needed, thanks! `Assert.ThrowsAsync(async () => await this.eligibilityService.GetEligibility(eligibilityCriteriaModel)).Result;` – Todd Vance Dec 02 '16 at 20:27
  • 1
    @ToddVance: I'd recommend using `await` instead of `Result`. – Stephen Cleary Dec 04 '16 at 14:12