Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Async Methods, best to use WhenAll with different return types

I have an Async controller action method which calls 4 async methods on my Backend to return List from each. The List of objects is different for each method. ie List List etc.

I have this working as so:

BizProvider bp = new BizProvider();
List<biz.Customer> custReturn = await bp.GetCustomerAsync();
List<biz.Account> acctReturn = await bp.GetAccountAsync();
...plus 2 more
List<object> returnArr = new List<object>();
returnArr.Add(custReturn);
returnArr.Add(acctReturn);  ...plus 2 more
return JsonConvert.SerializeObject(returnArr);

Should I use Task.WhenAll because of multiple Tasks?

The example at the microsoft https://msdn.microsoft.com/en-us/library/hh194874%28v=vs.110%29.aspx

All the Tasks return List<int>

So I used and abstract class and had all my list object types inherit from this. and I changed the return type of my business object provider to return the List of abstract type so now I can do:

var tasks1 = new List<Task<List<Biz.AbstractClass>>>();
tasks1.Add(bp.GetCustomerAsAbstractAsync());
tasks1.Add(bp.GetAccountAsAbstractAsync());
...plus 2 more

I then call var continuation = Task.WhenAll(tasks1);

this executes then the next line it gets to the .Results but then stops executing???

foreach (var result in continuation.Result)
                                       ^ stops here

further on I call

returnArrays.Add(result1);
return JsonConvert.SerializeObject(returnArrays);

but these never get hit...I don't know why. Perhaps I don't need a WhenAll, but then I am still curious what is going wrong. perhaps I need an await on the specific functions, or invoke the Action somehow, as in the Microsoft link the functions are called inline.

Task.Run(async () => { x=x,etc...)

Edit 20150306 => adding more implementation details

CustMan cm = new CustMan(); 
List<object> returnArr = new List<object>();
var aTask = cm.GetCustomersAsync(); 
var bTask = cm.GetAccountsAsync(); 
await Task.WhenAll(aTask, bTask);  
returnArr.Add(aTask.Result);//same for bTask <-- breakpoint never hits 
return JsonConvert.SerializeObject(returnArr); 
//also .js ajax return method never comes back.

//in CustMan()
public async Task<List<biz.Customer>> GetCustomersAsync() {
    List<biz.Customer> custList = await (from contact in ObjectContextDb.GetData<da.ContactInfo>()//<--generic returns IQueryable
     join customerContact in ObjectContextDb.GetData<da.CustomerContact>() on contact.Id equals customerContact.ContactInfoID
     join customerOrg in ObjectContextDb.GetData<da.CustomerOrganisation>() on customerContact.OrgID equals customerOrg.Id
     orderby contact.LastName
     select new biz.Customer {
       CustomerContactInfo = new biz.Contact() {
         ID = contact.Id,
         WorkPhone = contact.WorkPhone
       },
       CustomerOrg = new biz.CustomerOrganisation {
         ID = facultyOrg.Id,
         Name = facultyOrg.OrgName,
         ClientID = (customerContact.ClientID.HasValue ? customerContact.ClientID.Value : 0)
       }
     }).ToListAsync<biz.Customer>();
    return custList;// <-- Breakpoint hits here, the List has items
}

public async Task<List<biz.Account>> GetAccountsAsync()
{
 var roles = (from acctType in ObjectContextDb.GetData<da.AccountInType>()
  join r in ObjectContextDb.GetData<da.AccountType>() on acctType.AccountTypeID equals r.ID
  select new
  {
   AccountId = acctType.AccountID,
   Type = r.TypeName
  });   //.ToList();

  List<biz.Account> allContacts = await (from account in ObjectContextDb.GetData<da.Account>()
       orderby account.Name
       select new biz.Account()
       {
         Number = account.Id,
         Name = account.Name,
         Roles = (from r in roles where account.Id == r.AccountId select r.Type).ToList()
       }).ToListAsync<biz.Account>();

  return allContacts;//  <-- Breakpoint hits here, the List has items
}

The method which calls the Manager Class methods without WhenAll works!

CustMan cm = new CustMan(); 
List<object> returnArrays = new List<object>();
List<biz.Customer> custReturn = await cm.GetCustomersAsync();
List<biz.Account> acctReturn = await cm.GetAccountsAsync();

returnArrays.Add(custReturn);
returnArrays.Add(acctReturn);

return JsonConvert.SerializeObject(returnArrays);
like image 204
matthewbaskey Avatar asked Mar 05 '15 13:03

matthewbaskey


People also ask

Should I make every method async?

No, you should not. Making everything async hurts reading, writing and understanding your code, even if only a little. It could also hurt the performance of your code, especially if you really made every single method (does that include properties?) async .

Does task WhenAll run in parallel?

WhenAll() method in . NET Core. This will upload the first file, then the next file. There is no parallelism here, as the “async Task” does not automatically make something run in in parallel.

Does async await improve performance?

The main benefits of asynchronous programming using async / await include the following: Increase the performance and responsiveness of your application, particularly when you have long-running operations that do not require to block the execution.


2 Answers

You can use Task.WhenAll without changing anything. Just store the tasks in a variable and add their results afterwards:

var aTask = GetAAsync();
var bTask = GetBAsync();
...
await Task.WhenAll(aTask, bTask);
returnArr.Add(aTask.Result);
returnArr.Add(bTask.Result);  

In you case however, the async operations use Entity Framework which doesn't support multiple operations concurrently so you can't do that.

Call and await your operations one at a time:

returnArr.Add(await GetAAsync());
returnArr.Add(await GetBAsync());  
...
like image 66
i3arnon Avatar answered Nov 14 '22 23:11

i3arnon


It sounds like you're doing this:

var continuation = Task.WhenAll(tasks1);
foreach (var result in continuation.Result) { }

Which is probably deadlocking. WhenAll returns a Task too, so you'll need to await that:

var results = await Task.WhenAll(tasks1);
foreach (var result in results) { }

Otherwise, building a List of tasks and passing them into WhenAll is a perfectly reasonable way to await multiple tasks concurrently.

like image 29
Ant P Avatar answered Nov 15 '22 00:11

Ant P