Reputation: 545
I'm trying to get into the asynchronous thing. I'd like to make one of my methods asynchronous because it takes a while to finish so I tried this:
public static async Task GenerateExcelFile(HashSet<string> codes, ContestViewModel model)
{
var totalCodeToDistribute = model.NbrTotalCodes - (model.NbrCodesToPrinter + model.NbrCodesToClientService);
if (model.NbrTotalCodes > 0)
{
using (var package = new ExcelPackage())
{
await DoStuff(some, variables, here);
package.SaveAs(fileInfo);
}
}
}
So I could call it in my controller like that:
await FilesGenerationUtils.GenerateExcelFile(uniqueCodesHashSet, model);
but when it comes to the "await" keyword, it says that "Type void is not awaitable"
Is that a way to await for void methods or is it not a best practice? And if so, what would be the best way to do it?
The controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(ContestViewModel model)
{
var contentRootPath = _hostingEnvironment.ContentRootPath;
DirectoryUtils.OutputDir = new DirectoryInfo(contentRootPath + Path.DirectorySeparatorChar
+ "_CodesUniques" + Path.DirectorySeparatorChar
+ model.ProjectName +
Path.DirectorySeparatorChar
+ "_Codes");
var directory = DirectoryUtils.OutputDir;
var selectedAnswer = model.SelectedAnswer;
var uniqueCodesHashSet = new HashSet<string>();
try
{
while (uniqueCodesHashSet.Count < model.NbrTotalCodes)
{
var generatedString = RandomStringsUtils.Generate(model.AllowedChars, model.UniqueCodeLength);
uniqueCodesHashSet.Add(generatedString.ToUpper());
}
#region FOR TXT FILES
if (selectedAnswer == FileExtension.TXT.GetStringValue())
{
await FilesGenerationUtils.GenerateTxtFiles(uniqueCodesHashSet, model, directory);
}
#endregion
#region FOR XLSX FILES
if (selectedAnswer == FileExtension.XLSX.GetStringValue())
{
await FilesGenerationUtils.GenerateExcelFile(uniqueCodesHashSet, model);
}
#endregion
return View();
}
catch (Exception ex)
{
Console.Write(ex);
}
return View();
}
If I've understood what you're all saying, I must create a method that would be awaitable. Am I going right if I go with something like this:
public static Task DoStuff(ExcelWorksheet sheet, HashSet<string> codes, int rowIndex, int count, int maxRowValue)
{
foreach (var code in codes)
{
sheet.Row(rowIndex);
sheet.Cells[rowIndex, 1].Value = code;
rowIndex++;
count++;
if (rowIndex == maxRowValue && count < (codes.Count - 1))
{
sheet.InsertColumn(1, 1);
rowIndex = 1;
}
}
//What should be returned?!
return null;
}
Upvotes: 6
Views: 13451
Reputation: 1494
You can write async void methods but these cannot be awaited:
public static class Program
{
public static async Task Main()
{
const int mainDelayInMs = 500;
AsyncVoidMethod();
await Task.Delay(mainDelayInMs);
Console.WriteLine($"end of {nameof(Main)}");
}
static async void AsyncVoidMethod()
{
await Task.Delay(1000);
Console.WriteLine($"end of {nameof(AsyncVoidMethod)}");
}
}
As you can see AsyncVoidMethod is async but I cannot write await AsyncVoidMethod();
.
Async void methods should (most of the time) not be used as you cannot wait for completion of the task and any exception thrown may not be handled(and so it can crash your application): Why exactly is void async bad?
Upvotes: 10
Reputation: 348
There is generally little to no benefit to pretending your method is async when it blocks under the hood. If you need a task you can wrap the blocking method in Task.Run to create an awaitable task. You will still be using and blocking a thread, just not the current one.
Recommended reading: https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Upvotes: 3