I wanted to call / run three tasks parallely and then wait for their results. From my service class what I did is ...
var _validChapterCodesTask = gd.validateChapterCodeDetails(_input1);
var _validGroupCodesTask = gd.validateGroupCodeDetails(_input1);
await Task.WhenAll(_validChapterCodesTask, _validGroupCodesTask);
var _validChapterCodes = await _validChapterCodesTask;
var _validGroupCodes = await _validGroupCodesTask;
And in my DAL Class , the definitions look like :
public async Task<IEnumerable<ChapterCodeValidationOutput>> validateChapterCodeDetails(GroupMembershipValidationInput gmvi)
{
Repository rep = new Repository();
if (!gmvi._chapterCodes.All(x => x.Equals("")))
{
var _validChapterCodes = await rep.ExecuteSqlQueryAsync(typeof(ChapterCodeValidationOutput),SQL.Upload.UploadValidation.getChapterCodeValidationSQL(gmvi._chapterCodes), null);
return (IEnumerable<ChapterCodeValidationOutput>)_validChapterCodes;
}
else
return new List<ChapterCodeValidationOutput>();
}
public async Task<IEnumerable<GroupCodeValidationOutput>> validateGroupCodeDetails(GroupMembershipValidationInput gmvi)
{
Repository rep = new Repository();
if (!gmvi._chapterCodes.All(x => x.Equals("")))
{
var _validGroupCodes = await rep.ExecuteSqlQueryAsync(typeof(GroupCodeValidationOutput), SQL.Upload.UploadValidation.getGroupCodeValidationSQL(gmvi._groupCodes), null);
return (IEnumerable<GroupCodeValidationOutput>)_validGroupCodes;
}
else
return new List<GroupCodeValidationOutput>();
}
But these are not working as expected ? The breakpoints hit show that one by one the method is getting called from service, going to DAL, execute the DB Query and comeback and assign to respective variables.
And as soon as it hits
await Task.WhenAll(_validChapterCodesTask, _validGroupCodesTask);
The next few lines are not hit and the control returns to the invoking controller.
How would I get back their results and fire them parallely and wait for their results only?
What am I doing wrong ?
await Task.WhenAll(_validChapterCodesTask, _validGroupCodesTask)
creates a Task that should be waited for. Use await in the invoking controller.
Related
I am trying to call a stored procedure via EF 6. Using ToList() works as expected and a list of entities is returned. However, ToListAsync() does NOT seem to return data. In sql Profiler, I can see the stored procedure being executed against the database. However, the break point after the line for ToListAsync() does not get hit. I never see data get returned.
code below
public async Task<List<MyEntityObject>> GetStoredProcedureData() {
List<MyEntityObject> MyEntityObjects;
using (var dbContext = new DbContext())
{
var MyEntityObjectsQry = dbContext.Database.SqlQuery<MyEntityObject>("dbo.GetStoredProcedureData");
MyEntityObjects =await MyEntityObjectsQry.ToListAsync();
}
return MyEntityObjects;
}
I figured this out. I was not consuming the method correctly. I was using a windows service. Plus, I could not add async to the Main method.
static void Main(string[] args)
{
// async cannot be added to Main
// await CallStoredProcedure(); will not work here
Task<bool> task = Task.Run<bool>(async () => await CallStoredProcedure());
var test = task.Result;
}
static async Task<bool> CallStoredProcedure()
{
var dataService = new MyDataService();
// just call my method with await
var details = await dataService.GetStoredProcedureData();
Console.WriteLine("found {0} items", details.Count);
return true;
}
The get method will be like
var myEntityObjectsQry = await dbContext.Database.ToListAsync();
More references here:
https://www.entityframeworktutorial.net/entityframework6/async-query-and-save.aspx
Essentially I'm trying to be able to do this:
var thingTasks = thingFactory.GetMyThings();
// ...
var things = await thingTasks;
I'm trying to start from a list of objects, iterate through that list making an async call for each one, and returning the set of results in an await-able way so the consumer can choose when to await it. GetMyThings itself uses await before generating the list, so it needs to be async itself and is something like:
public async Task<List<Thing>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(async thing => await thing.GetAsync());
return things;
}
The basic idea is that I have some await lines, then after that I use the results of those lines to generate a list and generating each item also requires an async call. I'm trying to avoid blocking within the method (e.g. .Result) and instead pass that responsibility/opportunity back to the caller. Basically, start the tasks in the list but not await them. This naturally makes me want to return Task<List<Thing>> or 'List>`.
The closest I got was return Task.WhenAll(things) but that didn't work (it needed to be Task<Task<Thing[]>> and await await GetMyThings(). Alternatively, return Select(...) returning a Task<List<Task<Thing>>> and needing a await Task.WhenAll(await GetMyThings()) on the consuming side.
In both cases one needs double await statements to realize the list. I'm thinking it's impossible but is there a way to avoid the double await?
Use Task.WhenAll to await all task at once. This way you will run each GetAsync approximately at the same time. So :
Start all task
Await all
Return task's results
Like this :
public async Task<List<Thing>> GetMyThings()
{
var thingMakers = await GetThingMakers();
var tasks = thingMakers.Select(thing => thing.GetAsync());
var things = await Task.WhenAll(tasks);
return things.ToList();
}
If you want to make the inner tasks await-able outside, you need to actually return them:
public async Task<List<Task<Thing>>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(thing => thing.GetAsync());
return things.ToList();
}
You can then use this call like this:
List<Task<Thing>> thingTasks = await GetMyThings();
await Task.WhenAll(thingTasks);
List<Thing> = thingTasks.Select(t => t.Result).ToList();
Or even:
List<Thing> things = await GetMyThings()
.ContinueWith(async r =>
{
await Task.WhenAll(r);
return r.Select(r => r.Result).ToList();
});
i have a static method that should return list. but i want to do an await inside the method.
public static List<ContactModel> CreateSampleData()
{
var data = new List<ContactModel>();
StorageFolder musiclibrary = KnownFolders.MusicLibrary;
artists = (await musiclibrary.GetFoldersAsync(CommonFolderQuery.GroupByAlbumArtist)).ToList();
for (var i = 0; i < artists.Count; i++)
{
try
{
data.Add(new ContactModel(artists[i].Name));
}
catch { }
}
return data;
}
when i make it
public static async Task<List<ContactModel>> CreateSampleData(){//method contents}
i get error on another page for this code
Error: Task<List<ContactModel>> doesnt contain a definition for ToAlphaGroups
var items = ContactModel.CreateSampleData();
data = items.ToAlphaGroups(x => x.Name);
You have to await your async method:
var items = await ContactModel.CreateSampleData();
Your method now returns a Task, thats why you get the error message.
I don't know whether I should mention this because I agree with the answer of Jan-Patric Ahnen.
But since you said you cannot add await to your code: Task has a property called Result that returns the "result" of the Task.
var items = ContactModel.CreateSampleData().Result;
data = items.ToAlphaGroups(x => x.Name);
A few things before you use Result:
Result blocks the calling thread, if called from the UI thread your app might become unresponsive
You should try to avoid Result at all cost and try to use await since Result can produce unexpected results.
I have web api 2.1 service. Here is my Action :
public IHttpActionResult Get()
{
// Desired functionality :
// make e.g 5 request to `CheckSomething` with different parameter asynchronously/parallel and if any of them returns status Ok end request and return its result as result of `Get` action;
}
public IHttpActionResult CheckSomething(int id)
{
// some code
if(!something)
return NotFound();
return Ok(id);
}
What is best way to achieve this functionality ?
Put your tasks in an array and then call Task.WaitAny:
var finishedTask = Task.WaitAny(myTasks);
When it's finished finishedTask will be the index of the task in myTasks array that finished. You should then be able to get the result from it.
var result = myTasks[finishedTask].Result;
Actually, since you want to wait for the first to return Ok, I'd do something like this:
var taskList = new List<Task>() { ...your tasks ... };
while (taskList.Count > 0)
{
var idx = Task.WaitAny(taskList.ToArray());
if (taskList[idx].Result is Ok) // whatever the appropriate check is?
{
return taskList[idx].Result;
}
taskList.RemoveAt(idx);
}
// If you got here, none of your tasks returned ok
// so handle that however you want
I would use
Task.WhenAll
l to ensure that all task complete before continiuning.
here is an example:
var tasks = new Task[]
{
task1,
task2
};
await Task.WhenAll(tasks);
var task1Result = ((Task<Task1Result>)tasks[0]).Result;
var task2Result = ((Task<Task2Result>)tasks[1]).Result;
if all the task return the same result you don't need to cast, but you can also run task that return different result type in parallel for which you will need to cast back.
I have multiple async methods that return the same type from different OAuth based REST Api calls.
If I call one directly, I can get the data back:
//Call a specific provider
public async Task<List<Contacts>> Get()
{
return await Providers.SpecificProvider.GetContacts();
}
However, if I try to loop through multiple accounts, the object is returning before the AWAIT finishes:
//Call all providers
public async Task<List<Contacts>> Get()
{
return await Providers.GetContactsFromAllProviders();
}
public async Task<List<Contacts>> GetContactsFromAllProviders()
{
var returnList = new List<Contacts>();
//Providers inherits from List<>, so it can be enumerated to trigger
//all objects in the collection
foreach (var provider in Providers)
{
var con = await provider.GetContacts();
returnList.Add(con);
}
return returnList;
}
I'm new to async and am probably missing something simple
The code you have provided will call the web service for each provider one by one in a serial fashion. I do not see how your statement the object is returning before the AWAIT finishes can be true because inside the loop the call to GetContacts is awaited.
However, instead of calling the providers serially you can call them in parallel which in most cases should decrease the execution time:
var tasks = providers.Select(provider => provider.GetContacts());
// Tasks execute in parallel - wait for them all to complete.
var result = await Task.WhenAll(tasks);
// Combine the returned lists into a single list.
var returnList = result.SelectMany(contacts => contacts).ToList();