2

I have an action in controller in my ASP.NET Core Web API that uploads and processes a file. It's okay when file is small, but what if the file will be large? I want this action to execute asynchronously, so I defined method as async, its return value is just Task to be awaitable, and inserted continuous operations in await Task.Run(() => ...) blocks, made the action also async and call this method with await keyword, but, in fact, this action doesn't return after calling this method. I've tried to upload large file, and have had to wait until file is completely uploaded and processed. So, what should I do to make this action executed really asynchronously?

4
  • A method will not return immediately if it hits an await - await is a suspension point in the execution of the method until the awaited Task completes. Commented Sep 4, 2018 at 8:24
  • you should use async Task<IActionResult> please visit stackoverflow.com/questions/41953102/… Commented Sep 4, 2018 at 8:28
  • So, it is executed in a different task, but I still have to wait for it? How can I make this task background without waiting for it? Commented Sep 4, 2018 at 8:30
  • As i mentioned in question description, I've made the action async, respectively, I've also made its return value a Task<IActionResult> Commented Sep 4, 2018 at 8:32

2 Answers 2

5

The calling side of your Web Api project has no idea of C# details, of what a Task is or await means. It might be C# or JS or even PHP or a person that typed your URL in a browser bar.

Your Web Api will by default (and should!) wait until the whole processing is done and then give a signal that is was successful. This is generally done through a status code as return value.

Now the client calling your API does not have to be stuck waiting for that. But that's client side programming. You cannot really do anything in your API to solve the client's problem with that.

Otherwise, you could make your API just put the file in a processing queue to be processed by a different piece of software later. But that would need another architecture, because now the caller needs to be notified of success of failure (or result in general) when it's not longer connected to your API, so you'd need callbacks.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! Could you recommend some resources to learn about the processing queue in ASP.NET Core?
There is nothing specific to ASP.NET Core about queuing and processing. Your API writes the request to a queue (any kind you deem suitable... files, database, message queues, biztalk) and then another process operates on that queue and processes the items one by one. In the end, it needs a channel to notify the original caller. There is no single technology, but if you google "service bus", you might find some interesting topics.
Thank you! This is really helpful.
0

I had this code for processing large files (this was for a large CSV file upload):

    public async Task<IActionResult> UploadAsync(IFormFile file)
    {
        // Ensure the file has contents before processing.
        if (file == null || file.Length == 0)
            throw new ApiException("Csv file should not be null", HttpStatusCode.BadRequest)
                .AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.BelowMinimumLength, SOURCE); 

        // Ensure the file is not over the allowed limit.
        if (file.Length > (_settings.MaxCsvFileSize * 1024))
            throw new ApiException("Max file size exceeded, limit of " + _settings.MaxCsvFileSize + "mb", HttpStatusCode.BadRequest)
                .AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.ExceedsMaximumLength, SOURCE); 

        // Ensure the file type is csv and content type is correct for the file.
        if (Path.GetExtension(file.FileName) != ".csv" || 
            !Constants.CsvAcceptedContentType.Contains(file.ContentType.ToLower(CultureInfo.InvariantCulture)))
                throw new ApiException("Csv content only accepted").AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.Invalid, SOURCE);

        // Read csv content.
        var content = await file.ReadCsvAsync<OrderCsvResponseDto>() as CsvProcessedResponseDto<OrderCsvResponseDto>;

        await ProcessBulkUpload(content);

        // Return information about the csv file.
        return Ok(content);
    }

    internal async Task ProcessBulkUpload(CsvProcessedResponseDto<OrderCsvResponseDto> content)
    {
         // do some processing...
    }

There are web.config settings to increase the allowed time for file upload, this might help: How to increase the max upload file size in ASP.NET?

If your request exceeds the max timeout allowed data wont be returned to your caller as expected!

If you want to execute "fire-and-forget" code from C#, you can do this:

public static void FireAndForget(this Task task)
{
    Task.Run(async() => await task).ConfigureAwait(false);
}

Javascript:

xhr.onreadystatechange = function() { xhr.abort(); }

AngularJS:

var defer = $q.defer();
$http.get('/example', { timeout: defer.promise }).success(callback);
// [...]
defer.resolve();

Some async/await tips for Js: http://2ality.com/2016/10/async-function-tips.html

3 Comments

I have almost same code, but it is still not asynchronous. The problem is that I have to wait for my method to be completed, and I want to send uploaded file into method, and just return Ok ActionResult from action.
If you execute the call via script in the browser, do you see it timing out after a couple of minutes? I'd try the web.config setting and see if that helps :)
No, there's nothing in the browser. You see, code is correct and it executes correctly, I just want user not to wait it completely executed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.