How to make an existing code block async in .net? - c#

I have an existing code block with Linq to SQL queries and HTTP requests, that I'd like to make async for the purpose of using less threads.
Will it suffice to put the code block in an async method, like so?
public async Task<Customer> ProcessACustomer()
{
return await GetCustomer();
}
public Task<Customer> GetCustomer()
{
// Linq to SQL query here
// HTTP request here
Customer customer;
return Task.FromResult<Customer>(customer);
}
...or do I have to make every piece of logic in GetCustomer() async to accomplish this?
My hurdle here is that I have a ton of logic in that method (oversimplified above), so time will be an issue. Also, I can't seem to convert my Linq To SQL queries to async, as the async extension methods are not available for some reason (System.Data.Linq.Table does not contain a definition for FirstOrDefaultAsync() f.ex.).

Will it suffice to put the code block in an async method, like so?
No. The GetCustomer method is not asynchronous. It synchronously performs its work and then returns a completed task. This will not save any threads.
do I have to make every piece of logic in GetCustomer() async to accomplish this?
If you want to convert to async, then you should start at the opposite end. Don't start with the goal of making ProcessACustomer (or GetCustomer) asynchronous. Instead, start with the lowest-level API. Whatever your db access method is, make that asynchronous first and then let the async grow out from there.
My hurdle here is that I have a ton of logic in that method (oversimplified above), so time will be an issue.
This is a classic tradeoff. It may be worthwhile to convert to async, or it may be worthwhile to buy a few more servers instead.

Related

Keep Task and Task.FromResult if method is synchronous or remove Task-stuff at all?

I have a Web API controller (ASP.NET Core 5). Some of my APIs are asynchronous and some of them not. My question is next: Is there any advantages of using public **Task<T>** WebApiMethod + Task.FromResult() in Web API controllers if my method is synchronous?
For example:
public Task<string> GetSomeData()
{
// some synchronous processing...
return Task.FromResult(result);
}
Should I turn the example code above to something like this?
public string GetSomeData()
{
// some synchronous processing...
return result;
}
If the api is doing synchronous work it should have a synchronous signature. it will make the readability and understanding of the function clearer to other developers and will prevent misunderstanding when reading this function again in the future.
In the future, if the method will need to be modified to have an asynchronous task to do, you can change the signature to have an asynchronous signature.
You should use synchronous methods if the work is synchronous.
Yet, you should use Task signatures if you use some form of caching (e.g. with the proxy pattern). Then, of course if you already did the expensive I/O work, use Task.FromResult to return the cached result, and return the I/O task if it has to be newly downloaded.
It shouldn't have much difference in the way it is executed. If you can make the processing part asynchronous then it would make some difference.

C# Async when also needing support for synchronous call

I'm in a situation where we have some code that is run by user input (button click), that runs through a series of function calls and result in generating some data (which is a quite heavy operation, several minutes). We'd like to use Async for this so that it doesn't lock up the UI while we're doing this operation.
But at the same time we also have a requirement that the functions will also be available through an API which preferably should be synchronous.
Visualization/Example (pseudo-code):
public async void Button_Click() // from UI context
{
await instanceOfClassA.FuncA();
// some code
}
public async Task ClassA.FuncA()
{
await instanceOfClassB.FuncB()
// some code
}
public async Task ClassB.FuncB()
{
await instanceOfClassC.SomeHeavyFunc()
// some code
}
public async Task ClassC.SomeHeavyFunc()
{
// some heavy calculations
}
// Also need to provide a public synchronous API function
public void SomeClass.SynchronousAPIFunc()
{
// need to call differentInstanceOfClassB.FuncB()
}
Is there a way to make it so that the public API function does the waiting for the async operation internally?
EDIT:
In this post, user Rachel provides two answers to the question. Both seem interesting, though I'm unsure which one would offer the least amount of risk/side effects.
EDIT2:
I should note that we're using .NET v4.6.1.
Thanks in advance.
The problem with making "synchronous" versions of your methods that just call the asynchronous versions is that it can cause deadlocks, especially if the person calling this code is not aware that this is what is happening.
If you really want to make synchronous versions, then follow Microsoft's lead and write completely new methods that do not use any asynchronous code. For example, the implementation for File.ReadAllLines() doesn't use any of the same code as File.ReadAllLinesAsync().
If you don't want to do that, then just don't provide synchronous versions of your methods. Let the caller make the decision on how to deal with it. If they want to block synchronously on it, then they can mitigate the risk of deadlock.
But at the same time we also have a requirement that the functions will also be available through an API which preferably should be synchronous.
If you have the need to expose both a synchronous and asynchronous API, I recommend the boolean argument hack. This looks like:
public Task<T> FuncBAsync() => FuncBAsync(sync: false);
public T FuncB() => FuncBAsync(sync: true).GetAwaiter().GetResult();
public async Task<T> FuncBAsync(bool sync)
{
// Note: is `sync` is `true`, this method ***must*** return a completed task.
...
}
Is there a way to make it so that the public API function does the waiting for the async operation internally?
I do not recommend using direct blocking (e.g., GetAwaiter().GetResult()), as the straightforward implementation will lead to deadlocks.
EDIT: In this post, user Rachel provides two answers to the question.
I strongly recommend against using that solution. It uses a nested message loop with a custom SynchronizationContext, but doesn't do COM pumping. This can cause problems particularly if called from a UI thread. Even if the pumping isn't a problem, this solution can cause unexpected re-entrancy, which is a source of countless, extremely subtle, and difficult-to-find bugs.
You can utilize .GetAwaiter().GetResult()
as per your example, it would look like:
public void SomeClass.SynchronousAPIFunc()
{
// need to call differentInstanceOfClassB.FuncB()
ClassB.FuncB().GetAwaiter().GetResult();
}
Also, a good reference on when to not use the above can be found at Dont Block on Async Code

Should normal methods inside of an async function be also async?

I have an api end point that is async and then I'm awaiting fetch from database. If I want to use a helper method after the fetching is done of type like calculate something, does it have to be async aswell(it does not call any database, beside using the data that was fetched and makes some calculations)
If it has to be async, how do I do force things to be async then?
Like this?
await Task.Run(() => {
Calculate();
});
Or do I just leave it sync instead.
var numbers = await repo.Numbers.ToListAsync();
Calculate(numbers);
Leave it synchronous if it isn't doing file or database I/O, or other potentially blocking operations.
Think about it this way: if what you ask were true, the entire .NET API would be async, there's no difference between your proposed Calculate example and something like Int32.TryParse or any other random API call.
What you're doing in your example is not ideal.
If your underlying call (Calculate in this case) is synchronous, and there are no other asynchronous operation calls within your API, then there is no point in marking your API as "asynchronous", unless you have to satisfy a contract.
On the side note, though, data retrieval is actually an IO operation, and it should be asynchronous. Instead of wrapping that IO logic in a synchronous "Calculate" method, I'd recommend updating it to be async (you can follow a convention and name it CalculateAsync then). That will allow you to eliminate the Task.Run call as it'll be unnecessary.
To generalize this, an API should be async, if it consumes asynchronous logic. Otherwise - it should be synchronous.

C# caching and async requests

In this method:
private async List<stuff> GetData()
{
return await _appCache.GetAsync("SiteStructurePages",
() => _repository.GetSomeStuff());
}
is there any reason to keep it async? My code will execute in async manner just once, and all subsequent requests will use cache. Async spreads up to other parts of my application, that doesn't hit database, because lots of stuff is cached.
Edit:
My application is IIS website application.
is there any reason to keep it async?
That really depends on you. If you actually need to take advantage of asynchronous operations, which is being able to scale, then do keep it. Otherwise, if you don't, use a synchronous API. it's all up to you and what you actually make use of in your code base.
I understand the fact that you're worried about async methods going "all the way" and actually altering your entire code-base to become async. That's why you should do it as long as it's worth it.
Side note - you can slightly modify your code to save yourself the async state-machine generation:
private Task<List<stuff>> GetData()
{
return _appCache.GetAsync("SiteStructurePages",
() => _repository.GetSomeStuff());
}
If you can ensure that the call is quick and that it's not going to block a UI thread, then sure, you don't need Async on this one.
However, if such things are not in your control, then I would leave it asynchronous.

Awaiting last method call

A few posts on stack overflow have suggested the following:
Any async method where you have a single await expression awaiting a Task or Task < T >, right at the end of the method with no further processing, would be better off being written without using async/await.
Architecture for async/await
Await or Return
Is this advice just for particular circumstances? On a web server, isn't one of the main reasons to use async/await, being that while awaiting something like the UpdateDataAsync method below, the Thread will return to the ThreadPool, allowing the server to work on other requests?
Should SaveDataAsync await that DB update call given it's the last call in the method?
public async Task WorkWithDataAsync(Data data)
{
ManipulateData(data);
await SaveDataAsync(data);
await SendDataSomewhereAsync(data);
}
public async Task SaveDataAsync(Data data)
{
FixData(data);
await DBConnection.UpdateDataAsync(data);
}
Also, given you don't know where SaveDataAsync will be used, making it synchronous would hurt a method like WorkWithDataAsync would it not?
Removing the await and just returning the Task doesn't make the method synchronous. await is a tool that makes it easier to make a method asynchronous. It's not the only way to make a method asynchronous. It's there because it allows you to add continuations to task much more easily than you could without it, but in the method that you've shown you're not actually leveraging the await keyword to accomplish anything and as such you can remove it and have the code simply function identically.
(Note that technically the semantics of exceptions will have been slightly changed by removing async; thrown exceptions will be thrown from the method, not wrapped in the returned Task, if it matters to you. As far as any caller is concerned, this is the only observable difference.)
Writing these methods without the async-await keywords doesn't make them synchronous.
The idea is to return the task directly instead of having the overhead of generating the state machine.
The actual difference is about exception handling. An async method encapsulates exceptions inside the returned task while a simple task returning method doesn't.
If for example FixData throws an exception it will be captured in an async method but directly thrown in a simple task returning one. Both options would be equally asynchronous.

Categories