0

I am trying to use asynchronous programming with async and await to calculate some values and save in a database periodically using ASP.NET Core 2.x. The method shown below is not executing from the thread pool.

public async void CalculateCIndex(int CIndexId)
{
    List<string> UserIds = _context.ApplicationUsers
                                   .Where(m => m.EmailConfirmed == true)
                                   .Select(m => m.Id).ToList();

    for (int i = 0; i < UserIds.Count; i += 10)
    {
        var temp = UserIds.Skip(i).Take(10);

        foreach (var Id in temp)
        {
            await Task.Run(async () =>
            {
                await CIndexCal(Id, CIndexId);
            });  
        }
        await Task.Delay(6000);
    }
}

public async Task CIndexCal(string Id, int CIndexId)
{
    _context.CIndexMember.Add(new CIndexMember
    {
        NCIndex = (decimal)((AindexAfterRar + Power) / CI.TotalCindex)
    });

    _context.SaveChanges();
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Dan
  • 343
  • 3
  • 9
  • 21
  • 1
    "_The method shown below is not executing from the thread pool._" What is happening? – Johnathan Barclay Sep 28 '20 at 09:51
  • 1
    How you know that it is not executing? – Fabio Sep 28 '20 at 09:51
  • Its not saving values in the DB – Dan Sep 28 '20 at 09:56
  • 1
    Youtr `CIndexCal` method does not have `await` keyword in the body; thus it executes synchronously, isn't it? – kosist Sep 28 '20 at 09:56
  • 1
    Shouldn't you be using `SaveChangesAsync`? – stuartd Sep 28 '20 at 09:57
  • 1
    [Avoid async void](https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#avoid-async-void). – Theodor Zoulias Sep 28 '20 at 10:15
  • @Dan what are you trying to do with this code? You won't make an inefficient query run faster if you execute it multiple times in parallel, you'll make things *worse*. In this case, you are performing on the *client* something that should be a simple `INSERT ... SELECT` on the server – Panagiotis Kanavos Sep 28 '20 at 10:23
  • This looks like [an XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) - you have a problem X and assumed Y is the solution. When that didn't work you asked about Y, not the real problem X. What is the *real*. problem? What does `CIndexMember` do and what's the point of having just a single `NCIndex` value? This looks like something that could be a simple computed column. Or not needed at all – Panagiotis Kanavos Sep 28 '20 at 10:27
  • Whatever the problem is, executing 54K inserts sequentially will be a lot slower than just adding all items to the context and calling `SaveChanges` just once. – Panagiotis Kanavos Sep 28 '20 at 10:30
  • Thank you Panagiotis Kanavos for your answer, would you mind helping me by looking at my code – Dan Sep 28 '20 at 10:49
  • @Dan I have, that's why I posted those comments. The problem is the code itself. You'd get better performance if you *didn't* use `Task.Run` and `CIndexCal` in the first place. You'd get even better performance if the table had the proper indexes though. This code looks like an attempt to "fix" some performance problem by creating a new "index". 54K rows is no data at all though - a SQL Server database can handle billions of rows per table. An INSERT with just 54K rows should only take a second. This code isn't fixing the original problem, it's creating a new one – Panagiotis Kanavos Sep 28 '20 at 11:07
  • How to contact you to have direct look at my code using teamviwer, please – Dan Sep 28 '20 at 11:09
  • A far better idea would be to [ask a good question](https://stackoverflow.com/help/how-to-ask), asking about the *actual* problem. Do you want to send batches of mass emails to all users for example? You could use eg `ROW_NUMBER()` or some other windowing function in SQL to partition data in the SQL query directly, without having to create intermediate index tables – Panagiotis Kanavos Sep 28 '20 at 11:11
  • I want to calculate user’s activity as value and compare to all users in the platform and provide them with unique score. Activities like Blog posts, answering questions, profile visitors etc...So the first thing to calculate those activities and save it in different table for all users. – Dan Sep 28 '20 at 11:15
  • Could you share your models?You said it does not save in db,so what is the error message when you save the items?You could add try/catch surrounding with the save operation. – Rena Sep 29 '20 at 09:23
  • I have solved the issue by using Hangfire, the service name is "Fire-and-forget jobs" var jobId = BackgroundJob.Enqueue( () => Console.WriteLine("Fire-and-forget!")); – Dan Sep 30 '20 at 11:51

1 Answers1

0

You can find good answer here: async/await - when to return a Task vs void?

Your method CIndexCal is declared as async but it is not async at all due to fact you are not using async methods inside. In order to change this you could change:

_context.SaveChanges();

to

await _context.SaveChangesAsync();

Then instead of:

await Task.Run(async () =>
                    {
                        await CIndexCal(Id, CIndexId);
                    }); 

you just need:

await CIndexCal(Id, CIndexId);

and I am not sure why you are making delay and I think you don't need this:

await Task.Delay(6000);
  • Thank you for your answer, It has excuted one calculation and save one record in the DB. I have 54k users more. – Dan Sep 28 '20 at 10:11
  • @Dan whatever you want to do, this is the wrong way to do it. You won't get better performance by trying to run a bad SQL query in parallel, quite the opposite. If you have 54K rows, you shouldn't be using an ORM in the first place. You have no Objects to Map, just rows with data – Panagiotis Kanavos Sep 28 '20 at 10:21
  • @Dan in this case, instead of pulling 54K rows, modifying them and sending them back, you should write a simple `INSERT` query to do what you want – Panagiotis Kanavos Sep 28 '20 at 10:25
  • I have complex calculation to calculate an User Index, I need to perform all those calculations then add a value for eachg user to the DB – Dan Sep 28 '20 at 10:43
  • Thank you @Panagiotis for your answer, would you mind helping me by looking at my code – Dan Sep 28 '20 at 11:00