Search code examples
c#.netcontrollerbackend

Run heavy task that uses dbContext on the API background without await


My C# .Net 6 API project has a reporting requirement: Convert any query or class to CSV file.

The way that I've got the tasks is as follows:

  1. in a [POST] request endpoint named export create the CSV file from a query and upload it to blob storage, without making the user wait for the task to finish.
  2. once the controller gets the requests start the task and return 200 immediately.
  3. later on frontEnd will make a get request and ask for the document, if the document is done, return the document URL.

This is the endpoint code that I have so far:

[HttpPost("export")]
        public virtual async Task<IActionResult> Export([FromQuery] UrlRequestBase? urlRequestBase,
            [FromBody] BodyRequestBase? bodyRequestBase)
        {
            object? response;

            int status = 200;
            try
            {
                await urlRequestBase.Parse(this);

                await bodyRequestBase.Parse();

                //Run the export creation in another thread

                Task.Run(() => _repositoryBase.CreateExport(urlRequestBase, bodyRequestBase));


                return StatusCode(status);

            }
            catch (ExceptionBase ex)
            {
                return StatusCode(ex.CodeResult, ex.CreateResponseFromException());
            }


        }

The problem is that when I try to make a query inside the repository the dbContext is disposed of because of the lifetime of the DI container, so I get the following error:

Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.

it only works when I add await operator but is intended to not wait this time.

How can I run this type of heavy task without await operator and still use the dbContext?

Exists a better way to do it?


Solution

  • The FastAI response was so helpful but unfortunately, I had almost no time to implement it.

    DavidG recommended me to use HangFire and now my issue is finally solved

    [HttpPost("export")]
            public virtual async Task<IActionResult> Export([FromQuery] UrlRequestBase? urlRequestBase,
                [FromBody] BodyRequestBase? bodyRequestBase)
            {
                object? response;
                int status = 200;
                try
                {
                    await urlRequestBase.Parse(this);
    
                    await bodyRequestBase.Parse();
    //HangFire solution
                    _backgroundJobClient.Enqueue(() => _repositoryBase.CreateExport(urlRequestBase, bodyRequestBase));
                    
    
                    return StatusCode(status);
                }
                catch (ExceptionBase ex)
                {
                    return StatusCode(ex.CodeResult, ex.CreateResponseFromException());
                }
    
                return StatusCode(status, await ResponseBase.Response(response));
            }
    

    Thank you, for taking the time to help me!