Home > Back-end >  async or not async method
async or not async method

Time:02-19

I am writing a blazor server web application.

This application works with a database and Entity Framework.

Here is a method i've wrote:

private void MyMethod()
{
    var query = mydbcontext.mytable1.Where(t => ...);
    foreach (var item in query)
    {
       ...
    }
}

As you can see this method is not declared with "async Task". So she will be called without "await" keyword.

I can declare it with "async Task" and call it with "await". It works but it gives me a warning because i have no async call inside.

Let's suppose i decide to not declare it with "async Task" in order to avoid the warning.

What will happen if i need to change something in my function later which needs an Async call (For example this):

private async Task MyMethod()
{
    var query = mydbcontext.mytable1.Where(t => ...);
    foreach (var item in query)
    {
       ...
    }
    var countresult = await query.CountAsync();
}

I will need to search all calls of MyMethod and add "await" on each of this calls.

To prevent that, I am wondering if i should not declare all my methods with "async Task". But this is ugly because i will get warnings.

What is the best practice ?

And is there a way to do this loop as ASync ?

private async Task MyMethod()
{
    var query = mydbcontext.mytable1.Where(t => ...);
    await foreachAsync (var item in query)
    {
       ...
    }
    var countresult = await query.CountAsync();
}

Thanks

CodePudding user response:

... I am wondering if i should not declare all my methods with "async Task".

If there is no I/O being performed by the method then there is no need to make the method return a Task or Task<T> and by extension no need to use the async keyword.

If the method does do operations that use I/O (like a database call) then you have a couple of options and what you choose depends on the context your code is being used in.

When to use Asynchronous Operations

If the context can benefit from using asynchronous I/O operations, like the context of a web application, then your methods should return Task or Task<T>. The benefit of using asynchronous I/O is that threads are not locked waiting on I/O to complete which is important in applications that need to service many simultaneous requests that are thread resource intensive (like a web application). A windows forms or wpf application is also applicable because it allows for easy coding so the main thread can resume operations while I/O completes (the ui won't appear frozen).

When not to use Asynchronous Operations

If the context cannot benefit from using asynchronous I/O operations then make all your methods synchronous and do not return Task or Task<T>. An example of this would be a console application or a windows service application. You mentioned a Blazor web application in your question so this scenario does not appear to apply to you.

Provide both Asynchronous Operations and Synchronous Operations

If you want to allow the caller to choose then you can include 2 variations for each call that executes an I/O operation. A good example of this are the extensions written for Entity Framework. Almost all of the operations can be performed asynchronously or synchronously. Examples include Single/SingleAsync, All/AllAsync, ToArray/ToArrayAsync, etc.

This approach will also allow you to extend a public method later by adding an async overload if you extend the operation to include an I/O call at a future point in time. There are ways to do this without creating too much code duplication. See Async Programming - Brownfield Async Development by Stephen Cleary for various techniques. My favorite approach is the The Flag Argument Hack.


Side note: If you do not need to consume the results of an I/O call in your method and there is only one I/O method you do not need to use the async/await keywords. You can return the result of the I/O operation directly.

CodePudding user response:

private void MyMethod()
{
    var query = mydbcontext.mytable1.Where(t => ...);
    foreach (var item in query) // Here you will retrieve your data from the DB.
    {
       ...
    }
    
    // If you're going to count it here, don't use the query. You already retrieved 
    // the data for your foreach.
}

It would be better to retrieve the data before the foreach. That way you will not call the DB twice for 1. the foreach, 2. the count. I.E.

private async Task MyMethod()
{
    var data = await mydbcontext.mytable1.Where(t => ...).ToListAsync();
    foreach (var item in data)
    {
       ...
    }

    // Don't call the DB here. You already have the data, so just count the items in 
    // the list.
    var countresult = await data.Count; 
}
  • Related