Try to send a mail, but the task is cancelled. Any idea why?
public static Task SendAsync(this SmtpClient client, MailMessage message)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Guid sendGuid = Guid.NewGuid();
SendCompletedEventHandler handler = null;
handler = (o, ea) =>
{
if (ea.UserState is Guid && ((Guid)ea.UserState) == sendGuid)
{
client.SendCompleted -= handler;
if (ea.Cancelled)
{
tcs.SetCanceled(); // TASK CANCELLED: Why?
}
else if (ea.Error != null)
{
tcs.SetException(ea.Error);
}
else
{
tcs.SetResult(null);
}
}
};
client.SendCompleted += handler;
client.SendAsync(message, sendGuid);
return tcs.Task;
}
Called by:
using( SmtpClient smtpClient = new SmtpClient() )
{
return smtpClient.SendAsync(msg);
}
Thanks in advance for any help!
Gerard
A using statement will call an objects Dispose method when finishing the execution block. Calling smtpClient.SendAsync without using await on the async method will cause the execution block to end and Dispose will be called on SmtpClient, even though the SendAsync method is still executing, which explains why some mails finish as some may complete before disposing the objects and others dont.
Do this:
using (SmtpClient smtpClient = new SmtpClient())
{
await smtpClient.SendAsync(msg);
}
Related
Good day.
I'm having a problem exiting a task with the cancellation token.
My program freezes when I get to the token2.ThrowIfCancellationRequested();.
Following it with the breakpoints is shows that the token2 is cancelled, but the program doesn't revert back to the previous sub routine where I try and catch
try
{
Task.Run(() => SendData_DoWork(_tokenSource3));
}
catch (OperationCanceledException ex)
{
SetText("Communivation error with device");
SetText("");
}
finally
{
token.Dispose();
}
}//comms routine
//send Meter Address to communicate to meter
private void SendData_DoWork(CancellationTokenSource token)
{
var token2 = token.Token;
var _tokenSource4 = new CancellationTokenSource();
try
{
timer.Interval = 10000;
timer.Start();
timer.Elapsed += OnTimerElapsed;
NetworkStream stream = client.GetStream();
SerialConverter serialConverter = new SerialConverter();
Thread.Sleep(1000);
string newtext = null;
newtext = $"/?{address}!\r\n";
SetText("TX: " + newtext);
byte[] newData = stringSend(newtext);
stream.Write(newData, 0, newData.Length);
Thread.Sleep(50);
byte[] message = new byte[23];
int byteRead;
while (true)
{
byteRead = 0;
try
{
byteRead = stream.Read(message, 0, 23);
if (message[0] == (char)0x15)
{
token.Cancel();
}
}
catch
{
token.Cancel();
}
if ((byteRead == 0))
{
token.Cancel();
}
timer.Stop();
timer.Dispose();
ASCIIEncoding encoder = new ASCIIEncoding();
string newresponse = encoder.GetString(serialConverter.convertFromSerial(message));
SetText("RX: " + newresponse);
if (newresponse[0].ToString() == SOH)
{
token.Cancel();
}
if (newresponse != null)
{
/* NEXT SUB ROUTINE*/
}
else { break; }
}//while looop
}//try
catch (Exception ex)
{
token.Cancel();
}
if (token2.IsCancellationRequested)
{
timer.Stop();
timer.Dispose();
token2.ThrowIfCancellationRequested();
}
}//sendData subroutine
You are launching a Task, and ignoring the result; the only time Task.Run would throw is if the task-method is invalid, or enqueuing the operation itself failed. If you want to know how SendData_DoWork ended, you'll need to actually check the result of the task, by capturing the result of Task.Run and awaiting it (preferably asynchronously, although if we're talking async, SendData_DoWork should probably also be async and return a Task).
Your catch/finally will probably be exited long before SendData_DoWork even starts - again: Task.Run just takes the time required to validate and enqueue the operation; not wait for it to happen.
I think you have missunderstood how cancellation tokens are supposed to work. Your work method should take a CancellationToken, not a CancellationTokenSource. And it should call ThrowIfCancellationRequested inside the loop, not after. I would suspect that you would get some issues with multiple cancel calls to the same cancellation token.
Typically you would use a pattern something like like this:
public void MyCancelButtonHandler(...) => cts.Cancel();
public async void MyButtonHandler(...){
try{
cts = new CancellationTokenSource(); // update shared field
await Task.Run(() => MyBackgroundWork(cts.Token));
}
catch(OperationCancelledException){} // Ignore
catch(Exception){} // handle other exceptions
}
private void MyBackgroundWork(CancellationToken cancel){
while(...){
cancel.ThrowIfCancellationRequested();
// Do actual work
}
}
So in my particular case it seems like changing the sub-routines from private async void ... to private async Task fixes the particular issue that I'm having.
I need to change current code to not block current thread when EventWaitHandle.WaitOne is called. Problem is that I am awaiting system-wide event. I did not find any proper replacement yet.
Code:
EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.AutoReset, "Local event", out screenLoadedSignalMutexWasCreated);
StartOtherApp();
if (screenLoadedSignalMutexWasCreated)
{
isOtherAppFullyLoaded = handle.WaitOne(45000, true);
if (isOtherAppFullyLoaded )
{
// do stuff
}
else
{
// do stuff
}
handle.Dispose();
signalingCompleted = true;
}
else
{
isOtherAppFullyLoaded = false;
throw new Exception(" ");
}
I need app to continue and not stop on the line where I call WaitOne, ideally there would be await. How can I implement this ?
You can use AsyncFactory.FromWaitHandle, in my AsyncEx library:
isOtherAppFullyLoaded = await AsyncFactory.FromWaitHandle(handle,
TimeSpan.FromMilliseconds(45000));
The implementation uses ThreadPool.RegisterWaitForSingleObject:
public static Task<bool> FromWaitHandle(WaitHandle handle, TimeSpan timeout)
{
// Handle synchronous cases.
var alreadySignalled = handle.WaitOne(0);
if (alreadySignalled)
return Task.FromResult(true);
if (timeout == TimeSpan.Zero)
return Task.FromResult(false);
// Register all asynchronous cases.
var tcs = new TaskCompletionSource<bool>();
var threadPoolRegistration = ThreadPool.RegisterWaitForSingleObject(handle,
(state, timedOut) => ((TaskCompletionSource<bool>)state).TrySetResult(!timedOut),
tcs, timeout);
tcs.Task.ContinueWith(_ =>
{
threadPoolRegistration.Dispose();
}, TaskScheduler.Default);
return tcs.Task;
}
When new users register on my site I require them to verify their email by sending them an email with a unique link in it.
If I do this synchronously with the Controller Register Action it takes about 3 - 5 seconds for the page to return as the email method takes some time to complete.
In order to deal with this I am doing this:
Thread emailRequestThread = new Thread(() => (new EmailSender()).SendConfirmAdressEmail(user));
emailRequestThread.Start();
It is working but is this a bad idea?
If so how should I accomplish the same result?
Instead of spinning up a new Thread to send mail, i would go with the async approach.
What we do here is wrap the StmpClient.SendAsync EAP pattern with a Task so we can await using its TaskAwaitable:
public static Task SendAsyncTask(this SmtpClient client, MailMessage message)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Guid sendGuid = Guid.NewGuid();
SendCompletedEventHandler handler = null;
handler = (o, ea) =>
{
if (ea.UserState is Guid && ((Guid)ea.UserState) == sendGuid)
{
client.SendCompleted -= handler;
if (ea.Cancelled)
{
tcs.SetCanceled();
}
else if (ea.Error != null)
{
tcs.SetException(ea.Error);
}
else
{
tcs.SetResult(null);
}
}
};
client.SendCompleted += handler;
client.SendAsync(message, sendGuid);
return tcs.Task;
}
And then use it this way:
Task sendTask = await client.SendAsyncTask(message);
system.net.mail.smtpclient has two methods for which I am very confused.
1 . SendAsync(MailMessage, Object)
Sends the specified e-mail message to an SMTP server for delivery. This method does not block the calling thread and allows the caller to pass an object to the method that is invoked when the operation completes. -MSDN
2 . SendMailAsync(MailMessage)
Sends the specified message to an SMTP server for delivery as an asynchronous operation. -MSDN
Notice that the names of two methods are different so it is not an overload. What exactly is the difference here?
I am looking for very clear answer as the description given by MSDN for both methods is very ambiguous (at least for me it is.)
The difference is one SendMailAsync uses the new async/await technology and the other uses the old callback technology. And more importantly, the Object that's passed is simply passed into the event handler as the userState when the method completes.
Firstly, they both work asynchronously.
However, SendAsync has existed since .NET 2. In order to maintain backwards compatiblity whilst supporting the new Tasks system SendMailAsync was added.
SendMailAsync returns a Task rather than void and allows the SmtpClient to support the new async and await functionality if required.
//SendAsync
public class MailHelper
{
public void SendMail(string mailfrom, string mailto, string body, string subject)
{
MailMessage MyMail = new MailMessage();
MyMail.From = new MailAddress(mailfrom);
MyMail.To.Add(mailto);
MyMail.Subject = subject;
MyMail.IsBodyHtml = true;
MyMail.Body = body;
MyMail.Priority = MailPriority.Normal;
SmtpClient smtpMailObj = new SmtpClient();
/*Setting*/
object userState = MyMail;
smtpMailObj.SendCompleted += new SendCompletedEventHandler(SmtpClient_OnCompleted);
try
{
smtpMailObj.SendAsync(MyMail, userState);
}
catch (Exception ex) { /* exception handling code here */ }
}
public static void SmtpClient_OnCompleted(object sender, AsyncCompletedEventArgs e)
{
//Get the Original MailMessage object
MailMessage mail = (MailMessage)e.UserState;
//write out the subject
string subject = mail.Subject;
if (e.Cancelled)
{
Console.WriteLine("Send canceled for mail with subject [{0}].", subject);
}
if (e.Error != null)
{
Console.WriteLine("Error {1} occurred when sending mail [{0}] ", subject, e.Error.ToString());
}
else
{
Console.WriteLine("Message [{0}] sent.", subject);
}
}
//
}
//SendMailAsync
public class MailHelper
{
//
public async Task<bool> SendMailAsync(string mailfrom, string mailto, string body, string subject)
{
MailMessage MyMail = new MailMessage();
MyMail.From = new MailAddress(mailfrom);
MyMail.To.Add(mailto);
MyMail.Subject = subject;
MyMail.IsBodyHtml = true;
MyMail.Body = body;
MyMail.Priority = MailPriority.Normal;
using (SmtpClient smtpMailObj = new SmtpClient())
{
/*Setting*/
try
{
await smtpMailObj.SendMailAsync(MyMail);
return true;
}
catch (Exception ex) { /* exception handling code here */ return false; }
}
}
}
SendMailAsync a simple TAP wrapper for SendAsync
More info: Are the SmtpClient.SendMailAsync methods Thread Safe?
This question already has answers here:
Asynchronously sending Emails in C#?
(11 answers)
Closed 9 years ago.
namespace Binarios.admin
{
public class SendEmailGeral
{
public SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
public MailMessage msg = new MailMessage();
public void Enviar(string sendFrom, string sendTo, string subject, string body)
{
string pass = "12345";
System.Net.NetworkCredential smtpCreds = new System.Net.NetworkCredential(sendFrom, pass);
//setup SMTP Host Here
client.UseDefaultCredentials = false;
client.Credentials = smtpCreds;
client.EnableSsl = true;
MailAddress to = new MailAddress(sendTo);
MailAddress from = new MailAddress(sendFrom);
msg.IsBodyHtml = true;
msg.Subject = subject;
msg.Body = body;
msg.From = from;
msg.To.Add(to);
client.Send(msg);
}
}
}
I've this code, but i'd like to improve it in way that i could send mails asynchronous.
Could you suggest any idea to improve this piece of code or other way to do it.
I've tried asynchronous properties that visual studio suggested but couldn't use them.
SmtpClient allows you to send asynchronously, and uses events to notify you when the send completes. This can be unweildy to use, so you can create an extension method to return a Task instead:
public static Task SendAsync(this SmtpClient client, MailMessage message)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Guid sendGuid = Guid.NewGuid();
SendCompletedEventHandler handler = null;
handler = (o, ea) =>
{
if (ea.UserState is Guid && ((Guid)ea.UserState) == sendGuid)
{
client.SendCompleted -= handler;
if (ea.Cancelled)
{
tcs.SetCanceled();
}
else if (ea.Error != null)
{
tcs.SetException(ea.Error);
}
else
{
tcs.SetResult(null);
}
}
};
client.SendCompleted += handler;
client.SendAsync(message, sendGuid);
return tcs.Task;
}
To get the result of the send task you can use ContinueWith:
Task sendTask = client.SendAsync(message);
sendTask.ContinueWith(task => {
if(task.IsFaulted) {
Exception ex = task.InnerExceptions.First();
//handle error
}
else if(task.IsCanceled) {
//handle cancellation
}
else {
//task completed successfully
}
});
Wild guess, but SendAsync might do the job!
Change your code from:
client.Send(msg);
To:
client.SendAsync(msg);
more details
link1
link2