Rectangle 27 0

c .net (dotnet) core webapi and async method feedback wanted?


// Here no async operations are called, so Task is not necessary
public IActionResult Index(string name = _defaultName)
{
    if(!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    return Ok();
}
// No extra thread is called and the request thread is only 
// used in between "await" calls
public async Task<IActionResult> Get(string name = _defaultName)
{
    var result = await GetResultFromDatabase();

    return Ok(someModel);
}
Task<IActionResult>
public IActionResult Get(string name = _defaultName)
{
    // Don't await or run CPU intensive stuff via Task.Run
    var result = SomeCpuIntensiveOperation();

    return Ok(result);
}
public async Task<IActionResult> Get(string name = _defaultName)
{
    // runs async, no extra thread
    var valueFromDb = await GetValueFromDb();

    // Don't await or run CPU intensive stuff via Task.Run
    // runs sync, on request thread
    var result = SomeCpuIntensiveOperation(valueFromDb);

    return Ok(result);
}
  • Better control over http response, will not tie up the thread while executing the method, but will spin off another thread to compute the result, and no definition for the output type
  • Better control over the http response, but it will tie up the thread while executing the method, no definition for the output data type
  • No control over http response, headers, code, etc - all set by the framework in case of success or failure, return type is visible to run-time tools

For CPU bound operation, simply runt hem synchronously on the request thread.

Generally, async scales better, when you have more requests than available threads in the thread pool. But most small sized applications never reach that state, especially when it's an learning exercise or a proof of concept.

I am wondering whether it makes sense without such downstream async dependencies to pursue async webapi methods and the resulting a tad weird looking code.

If you have CPU bound operation (calculating some complicated math etc.), then do not start a new thread. This will mess up with how ASP.NET Core manages the thread pool and you will gain nothing, but will still have the overhead of context-switches and hence it will lower your performance instead of increasing it.

It's very unlikely you hit that limit unless you have a high-traffic website which handles 100s or 1000s of requests per second.

No idea about performance of dynamic, just test it. returning Dynamic or concrete type is same, in the end all of them will be wrapped around a IActionResult anyways. IActionResult is not only for http codes, but also for converting or processing the result stream. i.e. PhysicalFileResult will return a file as stream back to the client. As mentioned above, use IActionResult when you don't do async operations or cpu bound operations only.

No new thread will be started instead. When the async operation completes, a thread will be taken from the thread pool and operation continues.

Please read the Async Programming : Introduction to Async/Await on ASP.NET article on MSDN from Stephen Cleary. It is about ASP.NET MVC 5 (the old webstack, not the rewrite ASP.NET Core) but the principle still applies to ASP.NET Core.

Thank you for the feedback! "You shouldn't have to use dynamic ever" - it was not really a question about (not) using dynamic, unless you could share specific feedback on observed performance impact using dynamic in high-volume web/api apps. I am looking into poc for 10,000+ req/sec micro-services. Scalability is very important and ideally I am looking for a sound generic practice to recommend moving forward, e.g. use async Task<IActionResult> in use-cases a & b, and use IActionResult in use-cases d & e.

The request thread will be kept up for the duration of the action yes. But if you do not use any async operations (DB Access, a network connection to another website/webservice or read/write files to database), using it is fine.

This assumption is just wrong. Using Task alone with true async operations (I/O operations like reading from filesystem, network connections or access to database) will NOT SPAWN a new thread. The operation will be started and the thread returned to the thread pool.

Use Task<IActionResult> in all true async operations. Allocating/awaiting a task has a slight overhead (allocation of Task class and creation of a state machine when using await). In the end, you will have to do performance tests to decide it finally and it can't be answered here. The above explains how to use Task/async correctly in ASP.NET and ASP.NET Core. The most important correction of your assumption is that awaiting real Task do not spawn new thread and hence do not involve the "expensiveness" of starting a new thread!

You can also mix CPU bound and async operations

You shouldn't have to use dynamic ever, if you need to return arbitrary responses from an action, you can use

Note