0

Simple question: I have an ASP.NET Core app that queries a SQL Server database using EF Core.

The client calls a method on a wrapper class that calls the methods, which get the data from the database.

The get data methods are like this:

public List<Person> GetPersons(string name)
{
    // some logic to decide which table to query, bad design, I know

    var list = dbContext.Persons.ToList();

    // some more internal logic on the "list" variable.

    return list;
}

I want to make the ToList() call asynchronous and use ToListAsync() however I am having trouble grasping the idea of async/await in this context, because if I do:

public List<Person> GetPersons(string name)
{
    // some logic to decide which table to query, bad design, I know

    var list = await dbContext.Persons.ToListAsync();

    // some more internal logic on the "list" variable.

    return list;
}

in order to free the thread while it's waiting for the DB to return data (this can take a while) I would have to make the method async, meaning I'd have to change List<Person> to Task<List<Person>>, meaning I'd have to change the calling method to await it, as well, meaning it'd have to make that async, too.

At least, that's how the compiler suggests and how I thought it might work.

Baseline - is there a way to make the code await the getting of data, without having to make every single method up the chain async and await, as well?

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
Tessaract
  • 1,047
  • 7
  • 24
  • If you don't do async/await all the way to the top it means you're going to synchronously wait which basically makes changing only part of the call chain to async/await pointless. Unless you're OK with doing fire and forget somewhere along the line. – juharr Oct 20 '21 at 19:40
  • 6
    No, there isn't – nbokmans Oct 20 '21 at 19:40
  • Suppose there's a way to do that. Then, the caller does something like `GetPersons("foo"); NextMethod();`. What do you expect to happen between the call to `GetPersons` and the call to `NextMethod`? Do you _want_ `NextMethod` to start executing before `GetPersons` completes? If the answer is no, then, that means that you _want_ to await `GetPersons` (i.e., `await GetPersons();`). – 41686d6564 stands w. Palestine Oct 20 '21 at 19:46
  • 1
    This is why async code is referred to as viral. It tends to "infect" your call graph. See https://stackoverflow.com/q/9343594/182821 for some good reading on the possibilities and edge-cases to be aware of. – Chris Oct 20 '21 at 19:49
  • @41686d6564, The OP is asking for a way to make `GetPersons` not `Async`, Your premise is completely wrong. e.g (He wants to leverage the benefits of Async, and execute code while pulling data from EF. This is possible by spawning a task to do so, but it defeats the purpose because the overhead will cancel the benefits – johnny 5 Oct 20 '21 at 20:00
  • @johnny5 _"The OP is asking for a way to make GetPersons not Async"_ And how does my comment says otherwise? I'm confused. – 41686d6564 stands w. Palestine Oct 20 '21 at 20:05
  • You're making an assumption that they're not making it async, but still changing the return type to a `Task`. e.g (the method will still be asynchronous but it will not be waited on). The only wait for the method to remain `sync` would be for the the async code to execute on a seperate thread within the method). Basically, If the GetPersons was sync you would not have to worry about fire and forget from the parent method – johnny 5 Oct 20 '21 at 20:16
  • @johnny5 No, I'm not making that assumption. I said to the OP "suppose the exact thing you're trying to do (i.e., not change the signature of `GetPersons` but still call `await .ToListAsync()` inside of it) is possible" and then I asked them what they expect should happen in that case when the someone calls `GetPersons` followed by another statement. This was an attempt to make the OP review their logic. – 41686d6564 stands w. Palestine Oct 20 '21 at 20:24
  • @Chris The word "viral" suggests/implies that there is something bad happening. But asyncronously awaiting an I/O operation is almost always good. – AgentFire Oct 21 '21 at 14:50

1 Answers1

-2

Baseline - is there a way to make the code await the getting of data, without having to make every single method up the chain async and await, as well?

You can do it but it will block the thread.

var list = dbContext.Persons.ToListAsync().GetAwaiter().GetResult();

This comes with high risk of deadlocking and should be avoided.

So why don't you use async await in the first place? It is the correct ,safe and efficient and standard way to it.

Martin Meeser
  • 2,784
  • 2
  • 28
  • 41
  • The OP states their using `ASP.NET Core` how does this have the risk of `deadlocking` when the synchronization context has been removed from .Net-Core? – johnny 5 Oct 20 '21 at 19:55
  • 2
    Beyond deadlocking, it defeats the entire purpose of changing some of the code to be asynchronous if you're only ever going to synchronously block on it. If you're going to do that you may as well just have everything be synchronous from the start. This is adding complexity and overhead (and risks of errors) for no benefit. – Servy Oct 20 '21 at 19:56
  • 2
    This is like throwing the cart, buying a motor vehicle, and then having the horse pull the vehicle. This code is basically doing exactly what `ToList()` already does. – 41686d6564 stands w. Palestine Oct 20 '21 at 19:59
  • 1
    I dont get the downvotes I am clearly saying it should be avoided and the program should be async in the first place. However the Q gets answered, it is technically possible. BTW even some Microsoft examples do use GetAwaiter like in Startpp.cs – Martin Meeser Oct 20 '21 at 22:27
  • 1
    Which Q gets answered? The only Q I see is *"is there a way to make the code **await** the getting of data, without making the method async"*, with goal *"in order to **free the thread** while it's waiting for the DB to return data (this can take a while)*". The answer is simply "no". – Ivan Stoev Oct 21 '21 at 07:19