Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do some work after the response in ASP.NET Core

I have an ASP.NET Core website, using EFCore. I would like to do some work like logging to the database, but after having sent the response to the user in order to answer faster.

I could do it in a different thread, but due to async access of the DbContext I am not sure it is safe. Is there any recommended way to do that?

public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
    try 
    {
      var newModel = new ResponseViewModel(model);
      // Some work 
      return View("RequestView",newModel)
    }
    finally
    {
        // Some analysis on the request
        // I would like to defer this part
        await Log(model);
    }
}

One of the reason is that I would like to call a web-service (geocoding), which is not needed to answer, but good to work on the log (I need the city/country of coordinates).

like image 948
Jean Avatar asked Jan 16 '17 00:01

Jean


People also ask

How many requests per second can ASP.NET Core handle?

7+ Million HTTP requests per second from a single server.

Why is ASP.NET Core so fast?

One of the reasons ASP.NET Core is faster is its extensive use of asynchronous patterns within the new MVC and Kestrel frameworks.

What is sync and async in .NET Core?

Async only buys you anything if your server is at load. It's only at times when your server is stressed that async will give it some much needed breathing room, whereas sync might bring it to its knees. It's all about scale.


2 Answers

I see this has never been answered, but actually have a solution. The simple solution:

public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
    try 
    {
      var newModel = new ResponseViewModel(model);
      // Some work 
      return View("RequestView",newModel)
    }
    finally
    {
        Response.OnCompleted(async () =>
        {
            // Do some work here
            await Log(model);
        });
    }
}

The secure solution, as OnCompleted used to be called before the response being sent, so delaying the response:

public static void OnCompleted2(this HttpResponse resp, Func<Task> callback)
{
    resp.OnCompleted(() =>
    {
        Task.Run(() => { try { callback.Invoke(); } catch {} });
        return Task.CompletedTask;
    });
}

and call Response.OnCompleted2(async () => { /* some async work */ })

like image 125
Jean Avatar answered Sep 17 '22 13:09

Jean


Building on Jeans answer and a question and answer on the try - return - finally pattern, the try and finally blocks can be removed (if you don't really want to catch an exception).

This leads to the following code:

public async Task<IActionResult> Request([FromForm] RequestViewModel model, string returnUrl = null)
{
    var newModel = new ResponseViewModel(model);

    // Some work 

    Response.OnCompleted(async () =>
    {
        // Do some work here
        await Log(model);
    });

    return View("RequestView", newModel);
}
like image 24
Johannes Buchholz Avatar answered Sep 18 '22 13:09

Johannes Buchholz