Home > Back-end >  Adding items to a list from different asynchronous method?
Adding items to a list from different asynchronous method?

Time:09-08

Help me please. I just created 3 requests on 3 table in my database and I need to run this code:

        var totals = new List<StockTransaction>();
        totals.AddRange(GetStockIn(siteCode));
        totals.AddRange(GetStockOut(siteCode));
        totals.AddRange(GetStockAdjustment(siteCode));

        private async Task<List<StockTransaction>> GetStockIn(string site)
        {
            return DbContext.StockIn
                .Where(y => y.StkInID.Substring(0, 4) == site)
                .ToList()
                .Select(y => new StockTransaction
                {
                    ItemCode = y.prodID,
                    SiteCode = site,
                    TransactionDate = y.StkInDate.Value,
                    Movement = "Stock in",
                    // And more
                })
                .ToList();
        }

        // GetStockOut and GetStockAdjustment are exactly the same than StockIn with minor adjustments.

Now, how can I dynamically add all these 3 queries to my list of stock transaction and run them in parallen, wait for end of 3 request, continue the code?

I suppose I need to use await but only once after the 3 calls. Maybe I must create a list of tasks and not a list of stock transactions. I'm a little bit lost.

Can you help me?

I already tried this

            var totals = new List<StockTransaction>();
            var task1 = GetStockIn(siteCode);
            var task2 = GetStockOut(siteCode);
            var task3 = GetStockAdjustment(siteCode);
            await Task.WhenAll(task1, task2, task3);
            totals.AddRange(task1.Result);
            totals.AddRange(task2.Result);
            totals.AddRange(task3.Result);

But it is not working. I see the 3 lines before the await take time one by one then the 3 line after await are executed very quickly.

EDIT

I also tried

        private async Task<List<StockIn>> GetStockIn(string site)
        {
            return DbContext.StockIn
                .Where(y => y.StkInID.Substring(0, 4) == site)
                .ToListAsync();
        }

Is also not working. I don't get it.

CodePudding user response:

your query need to actually be asynchronous to have any chance of being run in parallel

private async Task<List<StockTransaction>> GetStockIn(string site)
        {
            return (await DbContext.StockIn
                .Where(y => y.StkInID.Substring(0, 4) == site)
                .ToListAsync())
                .Select(y => new StockTransaction
                {
                    ItemCode = y.prodID,
                    SiteCode = site,
                    TransactionDate = y.StkInDate.Value,
                    Movement = "Stock in",
                    // And more
                })
                .ToList();
        }

The await will split the method into two parts, the first part will do the database call and then return, before the call completes, the second will only be run after the database call has completed, and only then will the task be completed.

Doing any kind of concurrent queries will of course require different database connections, i.e. different dbContext objects. So you should perhaps wrap your queries in a class that creates the context, do the query, and disposes the context. This follows the idea of using short lived context objects, and relying on connection pooling to avoid recreating connections for each context.

When waiting for the result you need to use the appropriate overload that takes an array or list of tasks

await Task.WhenAll( new []{task1, task2, task3});

CodePudding user response:

Your inner functions should be something like,

private async Task<IReadOnlyList<StockTransaction>> GetStockIn(string site)
{
    return await DbContext.StockIn
        .Where(y => y.StkInID.StartsWith(site))
        .Select(y => new StockTransaction
            {
                ItemCode = y.prodID,
                SiteCode = site,
                TransactionDate = y.StkInDate.Value,
                Movement = "Stock in",
                // And more
            })
        .ToListAsync();
}

Note, we call the awaitable Task<T> returning ToListAsync() method and await it in a async method.

Then you can combine your three versions using Task.WhenAll so they can run concurrently.

var combinedStockTransactions = (await Task.WhenAll(new[]
        {
            GetStockIn(siteCode),
            GetStockOut(siteCode),
            GetStockAdjustment(siteCode)
        })
    .SelectMany(st => st)
    .ToList();
    
  • Related