I have a function that sends out an email, like so:
public async Task SendEmail(string from, string to, string subject, string body, bool isBodyHtml = false)
{
await Task.Run(() =>
{
using (SmtpClient smtp = new SmtpClient(host, port))
{
smtp.Credentials = new NetworkCredential(userName, password);
smtp.EnableSsl = true;
smtp.SendCompleted += SmtpOnSendCompleted;
MailMessage message = new MailMessage(from, to, subject, body);
message.IsBodyHtml = isBodyHtml;
smtp.Send(message);
}
}).ContinueWith(task =>
{
LoggingService.Instance.BusinessLogger.Error(task.Exception.Flatten().InnerException);
}, TaskContinuationOptions.OnlyOnFaulted);
}
As you can see it is not a "true async", but rather a "deffered execution", so that I can call this method and it wouldn't block the current calling thread.
Now, I sometimes need a way to wait for the email to be sent, before proceeding. So I call my SendMail() method like so:
EmailService.Instance.SendEmail("[email protected]", "[email protected]", "Subject", "Body text").Wait();
with a .Wait() at the end.
For some reason using .Wait() - trying to force synchronous execution, cause exception:
System.Threading.Tasks.TaskCanceledException: A task was canceled
Questions:
1) Why am I getting this exception?
2) How do I force synchronous execution of this method?
Thanks
1) Why am I getting this exception?
You are getting the exception because,
2) How do I force synchronous execution of this method?
You can force synchronous execution by,
e.g.
var task = new Task(() => { ... });
task.RunSynchronously();
Check how the below program behaves when you throw an error in the original Task and when the original Task completes without any faults by commenting/uncommenting the dummy exception. You can execute the below program at http://rextester.com/
using System;
using System.Threading.Tasks;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
try
{
DoSomething().Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.InnerException.Message);
}
Console.WriteLine("DoSomething completed");
}
public static async Task DoSomething()
{
await Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine("Doing Something");
// throw new Exception("Something wen't wrong");
}).ContinueWith(task =>
{
Console.WriteLine(task.Exception.InnerException.Message);
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
}
If you're only logging the exception when anything goes wrong using the ContinueWith method, then you can get rid of that ContinueWith and put a try catch block inside the original Task to catch any exceptions and log them.
static void Main(string[] args)
{
DoSomething().Wait();
Console.WriteLine("DoSomething completed");
Console.ReadKey();
}
public static async Task DoSomething()
{
await Task.Factory.StartNew(() =>
{
try
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine("Doing Something");
throw new Exception("Something wen't wrong");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
});
}
Otherwise, if you want to do some additional work after original Task completes you can do it as follows.
namespace SO
{
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
DoSomething().Wait();
Console.WriteLine("DoSomething completed");
Console.ReadKey();
}
public static async Task DoSomething()
{
await Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine("Doing Something");
// throw new Exception("Something wen't wrong");
}).ContinueWith(task =>
{
if (task.Status == TaskStatus.Faulted)
{
// log exception
Console.WriteLine(task.Exception.InnerException.Message);
}
else if (task.Status == TaskStatus.RanToCompletion)
{
// do continuation work here
}
});
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With