Exception handling in asynchronous programming has always been a challenge. This was especially true in the catch blocks. As of C# 6, you are now allowed to write asynchronous code inside the catch
and finally
block of your exception handlers.
The application will simulate the action of reading a logfile. Assume that a third-party system always makes a backup of the logfile before processing it in another application. While this processing is happening, the logfile is deleted and recreated. Our application, however, needs to read this logfile on a periodic basis. We, therefore, need to be prepared for the case where the file does not exist in the location we expect it in. Therefore, we will purposely omit the main logfile, so that we can force an error.
BackupLog
folder. The MainLog
folder will remain empty:AsyncDemo
class, write a method to read the main logfile in the MainLog
folder:private async Task<int> ReadMainLog() { var bigFile = File.OpenRead(@"C: empLogMainLog askFile.txt"); var bigFileBuffer = new byte[bigFile.Length]; var readBytes = bigFile.ReadAsync(bigFileBuffer, 0, (int)bigFile.Length); await readBytes.ContinueWith(task => { if (task.Status == TaskStatus.RanToCompletion) Console.WriteLine("Main Log RanToCompletion"); else if (task.Status == TaskStatus.Faulted) Console.WriteLine("Main Log Faulted"); bigFile.Dispose(); }); return await readBytes; }
BackupLog
folder:private async Task<int> ReadBackupLog() { var bigFile = File.OpenRead(@"C: empLogBackupLog askFile.txt"); var bigFileBuffer = new byte[bigFile.Length]; var readBytes = bigFile.ReadAsync(bigFileBuffer, 0, (int)bigFile.Length); await readBytes.ContinueWith(task => { if (task.Status == TaskStatus.RanToCompletion) Console.WriteLine("Backup Log RanToCompletion"); else if (task.Status == TaskStatus.Faulted) Console.WriteLine("Backup Log Faulted"); bigFile.Dispose(); }); return await readBytes; }
In actual fact, we would probably only create a single method to read the logfiles, passing only the path as a parameter. In a production application, creating a class and overriding a method to read the different logfile locations would be a better approach. For the purposes of this recipe, however, we specifically wanted to create two separate methods so that the different calls to the asynchronous methods are clearly visible in the code.
ReadLogFile()
method that tries to read the main logfile. As we have not created the logfile in the MainLog
folder, the code will throw a FileNotFoundException
. It will then run the asynchronous method and await that in the catch
block of the ReadLogFile()
method (something that was impossible in the previous versions of C#), returning the bytes read to the calling code:public async Task<int> ReadLogFile() { int returnBytes = -1; try { Task<int> intBytesRead = ReadMainLog(); returnBytes = await ReadMainLog(); } catch (Exception ex) { try { returnBytes = await ReadBackupLog(); } catch (Exception) { throw; } } return returnBytes; }
namespace winformAsync { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { } } }
button1_Click
event and add the async
keyword to the click event. This is an example of a void
returning an asynchronous method:private async void button1_Click(object sender, EventArgs e) { }
AsyncDemo
class and attempt to read the main logfile. In a real-world example, it is at this point that the code does not know that the main logfile does not exist:private async void button1_Click(object sender, EventArgs e) { Console.WriteLine("Read backup file"); Chapter6.AsyncDemo oAsync = new Chapter6.AsyncDemo(); int readResult = await oAsync.ReadLogFile(); Console.WriteLine("Bytes read = " + readResult); }
Console.Writeline()
outputs as we have added them to the code in the Chapter6
class and in the Windows application.MainLog
folder. You will see that the exception is thrown, and the catch
block runs the code to read the backup logfile instead:The fact that we can await in catch
and finally
blocks allows developers much more flexibility, because asynchronous results can consistently be awaited throughout the application. As you can see from the code we wrote, as soon as the exception was thrown, we asynchronously read the file read method for the backup file.