StrugglingCoder
StrugglingCoder

Reputation: 5021

Running multiple tasks asynchronously and awaiting for their results c#

In my service layer I wanted to fire multiple methods asynchronously and await for their results.

I tried with one at a time and it is erroring out.

In my service class I called the method like

var _validChapterCodesTask = gd.validateChapterCodeDetails(_input1);

await Task.WhenAll(_validChapterCodesTask);

var _chapterCodeResult = await  _validChapterCodesTask;

And in DAL class the method definition looks like

 public async Task<IEnumerable<ChapterCodeValidationOutput>> validateChapterCodeDetails(GroupMembershipValidationInput gmvi)
 {
     Repository rep = new Repository();
     if (!gmvi._chapterCodes.All(x => x.Equals("")))
     {
         var _validChapterCodes = await rep.ExecuteStoredProcedureAsync<Entities.Upload.ChapterCodeValidationOutput>(SQL.Upload.UploadValidation.getChapterCodeValidationSQL(gmvi._chapterCodes),null);
         return _validChapterCodes;
     }
     else
         return new List<ChapterCodeValidationOutput>();
 }

Error message

Error 208 The await operator can only be used within an async method. Consider marking this method with the async modifier and changing its return type to Task<ARC.Donor.Business.Upload.GroupMembershipValidationOutput>. C:\Users\m1034699\Desktop\Stuart_Upgrade_2.1_New Approach\Stuart_Export_Upload_v2.1\Stuart Web Service\ARC.Donor.Service\Upload\UploadValidationServices.cs 34 13 ARC.Donor.Service

in lines

await Task.WhenAll(_validChapterCodesTask);

var _chapterCodeResult = await  _validChapterCodesTask; 

What am I doing wrong ?

Upvotes: 0

Views: 263

Answers (2)

David Pine
David Pine

Reputation: 24525

The error message is very explicit. It is telling you that you're Service class method is incorrectly attempting to use the async keyword. In order to fix this, you should be using "Async all the way" as defined in Stephen Cleary's post on MSDN as a best practice.

For example, if you're service class has a method body that is working with Task or Task<T>, or attempting to use the await keyword the method signature for this corresponding method body must be async (as the async keyword enables a method to use the await keyword). Additionally this method itself should also be Task or Task<T> returning, with very few exceptions to that rule.

Personally, I would alter your DAL class to simply return the operation that represents the asynchronous work without actually awaiting it. If you think about it, the body of the validateChapterCodeDetails method does not actually need to do anything with the results, it just needs to return them (instead of materializing them there). Consider the following:

public Task<IEnumerable<ChapterCodeValidationOutput>> 
    validateChapterCodeDetails(GroupMembershipValidationInput gmvi)
{
     var rep = new Repository();
     return gmvi._chapterCodes.All(x => x.Equals(""))
         ? new List<ChapterCodeValidationOutput>()
         : rep.ExecuteStoredProcedureAsync
               <Entities.Upload.ChapterCodeValidationOutput>
                   (SQL.Upload.UploadValidation
                              .getChapterCodeValidationSQL(gmvi._chapterCodes),null)
}

Since your Task<IEnumerable<ChapterCodeValidationOutput>> variable has already been awaited, you can access the .Result property to get what you're looking for. Then in your Service class your method would look like this:

public async Task ConsumeAsync()
{
    var _validChapterCodesTask = gd.validateChapterCodeDetails(_input1);

    await Task.WhenAll(_validChapterCodesTask);

    var _chapterCodeResult = _validChapterCodesTask.Result;
    // Do something with it...
}

Here is a .NET fiddle that should help to exemplify this for you better.

NOTE

I would also caution using IEnumerable as it pertains to your repo, you should ensure that the results from the Database are not occurring via deferred execution, otherwise you risk the potential for connection issues, i.e.; unintentionally leaving a connection open, or not properly closing one.

Upvotes: 1

wake-0
wake-0

Reputation: 3968

In the Service class where you call

var _validChapterCodesTask = gd.validateChapterCodeDetails(_input1);

await Task.WhenAll(_validChapterCodesTask);

var _chapterCodeResult = await  _validChapterCodesTask;

add a async to the method signature where await Task.WhenAll(_validChapterCodesTask); is called. And when this method also has a return type you have to wrap this type in a Task like the exception shows:

Task<ARC.Donor.Business.Upload.GroupMembershipValidationOutput>

E.g. if you have the following method:

public GroupMemberShipValidationOutput validate(..) {
   ...
   await Task.WhenAll(_validChapterCodesTask);
   ...
}

You have to change the method signature to:

public async Task<GroupMemberShipValidationOutput> validate(..) {
   ...
   await Task.WhenAll(_validChapterCodesTask);
   ...
}

Upvotes: 0

Related Questions