Home > OS >  EF core query loop asynchronous
EF core query loop asynchronous

Time:07-12

In the previous day I am looking for a way to make my code fully asynchronous. So that when called by a rest API, I' ll get an immediate response meanwhile the process is running in the background.

To do that I simply used tasks.Add(Task<bool>.Run( () => WholeProcessFunc(parameter) )) where WholeProcessFunc is the function that make all the calculations(it may be computationally intensive). It works as expected however I read that it is not optimal to wrap the whole process in a Task.Run.

My code need to compute different entity framework query which result depends on the previous one and contains also foreach loop. For instance I can' t understand which is the best practice to make async a function like this:

public async Task<List<float>> func()
{
    List<float> acsi = new List<float>();

    using (var db = new EFContext())
    {
        long[] ids = await db.table1.Join(db.table2 /*,...*/)
            .Where(/*...*/)
            .Select(/*...*/).ToArrayAsync();

        foreach (long id in ids)
        {
            var all = db.table1.Join(/*...*/)
                .Where(/*...*/);
            float acsi_temp = await all.OrderByDescending(/*...*/)
                .Select(/*...*/).FirstAsync();
            if (acsi_temp < 0) { break; }

            acsi.Add(acsi_temp);
        }
    }
    return acsi;
}

In particular I have difficulties with the foreach loop and the fact that the result of a query is used in the next . Finally with the break statement which I don't get how to translate it. I read about cancellation token, could it be the way ?

Is wrapping up all this function in a Task.Run a solid solution ?

CodePudding user response:

In the previous day I am looking for a way to make my code fully asynchronous. So that when called by a rest api, I' ll get an immediate response meanwhile the process is running in the background.

Well, that's one meaning of the word "asynchronous". Unfortunately, it's completely different than the kind of "asynchronous" that async/await does. async yields to the thread pool, not the client (browser).

It works as expected however I read that it is not optimal to wrap the whole process in a Task.Run.

It only seems to work as expected. It's likely that once your web site gets higher load, it will start to fail. It's definite that once your web site gets busier and you do things like rolling upgrades, it will start to fail.

Is wrapping up all this function in a Task.Run a solid solution ?

Not at all. Fire-and-forget is inherently dangerous.

A proper solution should be a basic distributed architecture:

  • A durable queue, such as an Azure Queue or Rabbit (if properly configured to be durable).
  • An independent processor, such as an Azure Function or Win32 Service.

Then the ASP.NET app will encode the work to be done into a queue message, enqueue that to the durable queue, and then return. Some time later, the processor will retrieve the message from that queue and do the actual work.

CodePudding user response:

You can translate your code to return an IAsyncEnumerable<...>, that way the caller can process the results as they are obtained. In an asp.net 5 MVC endpoint, this includes writing serialised json to the browser;

public async IAsyncEnumerable<float> func()
{
    using (var db = new EFContext())
    {
        //...

        foreach (long id in ids)
        {
            //...
            if(acsi_temp<0) { yield break; }

            yield return acsi_temp;
        }
    }
}

public async Task<IActionResult> ControllerAction(){
    if (...)
        return NotFound();
    return Ok(func());
}

Note that if your endpoint is an async IAsyncEnumerable coroutine. In asp.net 5, your headers would be flushed before your action even started. Giving you no way to return any http error codes.

Though for performance, you should try rework your queries so you can fetch all the data up front.

  • Related