How to understand C# SendAsync function? - c#

When i program with C# to send a mail by batch,my code is like this:
public static bool Send(MailAddress Messagefrom,
string MessageTo,
string MessageSubject,
string MessageBody)
{
MailMessage message = new MailMessage();
message.From = Messagefrom;
message.To.Add(MessageTo);
message.Subject = MessageSubject;
message.Body = MessageBody;
message.BodyEncoding = System.Text.Encoding.UTF8;
//message.SubjectEncoding = Encoding.BigEndianUnicode;
message.IsBodyHtml = true;
message.Priority = MailPriority.High;
MailHelper mh = new MailHelper();
SmtpClient sc = mh.setSmtpClient("smtp.qq.com", 25);
sc.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
try
{
sc.SendAsync(message, message);
}
catch (Exception e)
{
LogHelper.WriteLog("Main send failed....\t the detail info:" +
e.ToString());
return false;
}
return true;
}
This is a problem!When the first mail send failed(for example the mail address is null),the next mail can't be send!
Because i have so much mail wait to send,if this situation,how to fix it?For example the failed mail may still on this table and Administator to deal it by hand.
But this situation probably in Send function,Why this happen?

You have to catch errors in the foreach loop that calls your Send() function and log the errors somewhere:
foreach (var mail in mailsToSend)
{
try
{
// Call your send function
Send(...)
}
catch (Exception ex)
{
// Log the error somewhere (console, file, ...)
Console.WriteLine("Error sending mail {0}", mail);
}
}
This ensures that the application won't crash when one email fails to send and continue sending the other mails.
Also you should use Send() instead of SendAsync() in your Send() function. This is because the SendAsync function starts a new thread for sending the mail, while Send will stop your programs execution until the mail has been sent. One more reason you shouldn't use the SendAsync function is because according to microsoft only 1 mail can be send at a time. That means using the SendAsync function for more then 1 mail will cause it to throw an exception.

Related

C# smtpfailedrecipientsexception; How to exit function if this happens

So here is my code which is used to send emails from Unity using a C# script:
public void SendMail() // Mail send function
{
string emailAddress; // variable to store user inputted email
emailAddress = emailInput.text; // variable becomes the email the user types in
mail.From = new MailAddress("hiddenfornow");
mail.To.Add(emailAddress);
SmtpClient smtpServer = new SmtpClient("smtp.gmail.com");
smtpServer.Port = 587;
mail.Subject = "Test Subject" + currentDate;
mail.IsBodyHtml = true; // allows for html
mail.Body = "Testing testing";
smtpServer.Credentials = new System.Net.NetworkCredential("hiddenfornow", "hiddenfornow") as ICredentialsByHost;
smtpServer.EnableSsl = true;
SceneManager.LoadScene("TestScene"); // reloads the scene after user clicks button
ServicePointManager.ServerCertificateValidationCallback =
delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{ return true; };
smtpServer.Send(mail);
Debug.Log("success");
}
This code is working fine to send emails. However, if i enter an incorrect email i will recieve an "smtpfailedrecipientsexception" message.
After this, even entering a correct email address will not work. The smtpfailedrecipientsexception will continue to occur unless you type it correct the first time.
I would like to add some kind of If statement such as this which i've written in pseudocode:
If smtpserver.send(mail)returns smtp error
{
Exit this function
}
else
{
success message
}
I am just not sure how to implement this.
Use exception handling approach to dealing with runtime exception :
try
{
if (smtpserver.send(mail))
return "successful";
}
catch (SmtpFailedRecipientException ex)
{
// log your error
return ex.StatusCode; // return status code as you will know actual error code
}
finally
{
mail.Dispose(); // Dispose your mailmessage as it will clears any stream associated with your mail message such as attachment
}
Available Status Codes
The SmtpClient uses pooling to reduce the overhead of creating new connections to the server. (see: https://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient(v=vs.110).aspx#Remarks)
My assumption is that the SmtpFailedRecientsException is putting the connection into a bad state, so you need to force the connection to close by disposing the client:
public void SendMail() // Mail send function
{
//your code...
SmtpClient smtpServer = new SmtpClient("smtp.gmail.com");
try {
//.... your code continues.....
smtpServer.Send(mail);
Debug.Log("success");
} catch (SmtpFailedRecipientsException) { //or, perhaps any Exception
smtpServer.Dispose();
throw; //rethrow the exception, assuming you're handling it in the calling code
}
}
For future reference, here is the code which worked:
Try
{
smtpServer.Send(mail); // Attempts to send the email
Debug.Log("success");
}
catch (SmtpFailedRecipientsException) // Catches send failure
{
mail.Dispose(); // ends the SMTP connection
SceneManager.LoadScene("SceneName"); //Reloads the scene to clear textboxes
}

How to send email within in a loop with SMTP?

I have a console app thats sends mails within a thread.
in this thread method I have a loop that semds emails to each recipient.
I am having issues where as I am trying to send multiple emails before the previous ones have had the chance to get sent.
my code:
foreach(var m in mailModel.Recipients)
{
Mailmanager.SendMessageS(mailModel.DomainName, mailModel.Severity, DateTime.Now, m);
}
And the send method:
public static async Task SendMessageS(string domainName, ErrorSeverity severity, DateTime errorTime, Recipient recipient)
{
try
{
string error = "";
string fromEmail = "OwerWatch#mydomain.com";
string toEmail = recipient.SendEmailTo;
MailMessage message = new MailMessage(fromEmail, toEmail);
Guid guid = Guid.NewGuid();
SmtpClient smtpClient = new SmtpClient(server, port);
/*if (_useAuthentication)*/
smtpClient.Credentials = new NetworkCredential("", "");
smtpClient.EnableSsl = false;
//mail.Subject = subject;
//mail.Body = body;
message.Subject = "Problem ( " + severity + ") " + domainName;
message.Body = BuildMessage(error, recipient.RecipientName, domainName, errorTime, severity);
smtpClient.SendCompleted += SendCompletedCallback;
await smtpClient.SendMailAsync(fromEmail, toEmail, message.Subject, message.Body /* user state, can be any object*/);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I am getting this warrning:
because this call is not awaited execution of the current method
continues before the call is completed. Consider applying the await
opeartor to the result of the call
I understand the warrning but I can't figure out how I can accomplish this since I have a loop that goes through all my recipients.
How can I do this correctly?
This is a quick fix (added await keyword on your call to Mailmanager.SendMessageS), since your utility method is working doing async operation.
foreach(var m in mailModel.Recipients)
{
await Mailmanager.SendMessageS(mailModel.DomainName, mailModel.Severity, DateTime.Now, m);
}
There might be better ways to do it as an overall process.
The best thing to do here is to use a queue.
You should add all your messages to a queue and than process them one by one, even with multiple threads, if you want.
Check this answer for a couple of examples.

Send Email Async

This is what I've tried for sending email using the SendAsync() method. When passing the bool to send regular email it works fine. When sending with the SendAsync method no dice. Just looking for some tips if you see something wrong here. Thanks in advance.
private static void SendEmail(System.Net.Mail.MailMessage m, Boolean Async)
{
using (var smtpClient = new System.Net.Mail.SmtpClient(EmailList.SMTP_GOOGLE, 587))
{
smtpClient.EnableSsl = true;
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential("email#domain.com","password");
smtpClient.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
smtpClient.Timeout = 3000000;
if (Async)
{
object userState = m;
smtpClient.SendCompleted += new SendCompletedEventHandler(Smtp_OnComplete);
try
{
smtpClient.SendAsync(m, userState);
}
catch (Exception ex)
{
//Logging
}
}
else
{
try
{
smtpClient.Send(m);
}
catch (Exception ex)
//Logging
}
}
}
}
Your using statement is disposing the SmtpClient before the asynchronous send finishes.
That won't work.
Instead, you can either use C# 5 await to wait for the async send to finish inside the using statement, or get rid of using entirely for async sends and dispose the SmtpClient in the completion event.
I just set up a simple console app to run the methods for testing
Because the email is being sent asynchronously, the console app will start the method to in a different thread and continue with its own execution. If it closes before the method to send the actual email completes, the email will fail to send because Visual Studio will kill that thread. Try adding the following line after your call to send the email to make the console app wait a few seconds:
System.Threading.Thread.Sleep(5000);
This should be more than long enough for the email method to send the email and finish before the console app closes and Visual Studio kills all processes. The code should work fine on a web server.

Asynchronously sending Emails in C#?

I'm developing an application where a user clicks/presses enter on a certain button in a window, the application does some checks and determines whether to send out a couple of emails or not, then show another window with a message.
My issue is, sending out the 2 emails slows the process noticeably, and for some (~8) seconds the first window looks frozen while it's doing the sending.
Is there any way I can have these emails sent on the background and display the next window right away?
Please don't limit your answer with "use X class" or "just use X method" as I am not all too familiarized with the language yet and some more information would be highly appreciated.
Thanks.
As of .NET 4.5 SmtpClient implements async awaitable method
SendMailAsync.
As a result, to send email asynchronously is as following:
public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
var message = new MailMessage();
message.To.Add(toEmailAddress);
message.Subject = emailSubject;
message.Body = emailMessage;
using (var smtpClient = new SmtpClient())
{
await smtpClient.SendMailAsync(message);
}
}
As it's a small unit of work you should use ThreadPool.QueueUserWorkItem for the threading aspect of it. If you use the SmtpClient class to send your mail you could handle the SendCompleted event to give feedback to the user.
ThreadPool.QueueUserWorkItem(t =>
{
SmtpClient client = new SmtpClient("MyMailServer");
MailAddress from = new MailAddress("me#mydomain.com", "My Name", System.Text.Encoding.UTF8);
MailAddress to = new MailAddress("someone#theirdomain.com");
MailMessage message = new MailMessage(from, to);
message.Body = "The message I want to send.";
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Subject = "The subject of the email";
message.SubjectEncoding = System.Text.Encoding.UTF8;
// Set the method that is called back when the send operation ends.
client.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
// The userState can be any object that allows your callback
// method to identify this send operation.
// For this example, I am passing the message itself
client.SendAsync(message, message);
});
private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
// Get the message we sent
MailMessage msg = (MailMessage)e.UserState;
if (e.Cancelled)
{
// prompt user with "send cancelled" message
}
if (e.Error != null)
{
// prompt user with error message
}
else
{
// prompt user with message sent!
// as we have the message object we can also display who the message
// was sent to etc
}
// finally dispose of the message
if (msg != null)
msg.Dispose();
}
By creating a fresh SMTP client each time this will allow you to send out emails simultaneously.
It's not too complicated to simply send the message on a separate thread:
using System.Net.Mail;
Smtp.SendAsync(message);
Or if you want to construct the whole message on the separate thread instead of just sending it asynchronously:
using System.Threading;
using System.Net.Mail;
var sendMailThread = new Thread(() => {
var message=new MailMessage();
message.From="from e-mail";
message.To="to e-mail";
message.Subject="Message Subject";
message.Body="Message Body";
SmtpMail.SmtpServer="SMTP Server Address";
SmtpMail.Send(message);
});
sendMailThread.Start();
SmtpClient.SendAsync Method
Sample
using System;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading;
using System.ComponentModel;
namespace Examples.SmptExamples.Async
{
public class SimpleAsynchronousExample
{
static bool mailSent = false;
private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
// Get the unique identifier for this asynchronous operation.
String token = (string) e.UserState;
if (e.Cancelled)
{
Console.WriteLine("[{0}] Send canceled.", token);
}
if (e.Error != null)
{
Console.WriteLine("[{0}] {1}", token, e.Error.ToString());
} else
{
Console.WriteLine("Message sent.");
}
mailSent = true;
}
public static void Main(string[] args)
{
// Command line argument must the the SMTP host.
SmtpClient client = new SmtpClient(args[0]);
// Specify the e-mail sender.
// Create a mailing address that includes a UTF8 character
// in the display name.
MailAddress from = new MailAddress("jane#contoso.com",
"Jane " + (char)0xD8+ " Clayton",
System.Text.Encoding.UTF8);
// Set destinations for the e-mail message.
MailAddress to = new MailAddress("ben#contoso.com");
// Specify the message content.
MailMessage message = new MailMessage(from, to);
message.Body = "This is a test e-mail message sent by an application. ";
// Include some non-ASCII characters in body and subject.
string someArrows = new string(new char[] {'\u2190', '\u2191', '\u2192', '\u2193'});
message.Body += Environment.NewLine + someArrows;
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Subject = "test message 1" + someArrows;
message.SubjectEncoding = System.Text.Encoding.UTF8;
// Set the method that is called back when the send operation ends.
client.SendCompleted += new
SendCompletedEventHandler(SendCompletedCallback);
// The userState can be any object that allows your callback
// method to identify this send operation.
// For this example, the userToken is a string constant.
string userState = "test message1";
client.SendAsync(message, userState);
Console.WriteLine("Sending message... press c to cancel mail. Press any other key to exit.");
string answer = Console.ReadLine();
// If the user canceled the send, and mail hasn't been sent yet,
// then cancel the pending operation.
if (answer.StartsWith("c") && mailSent == false)
{
client.SendAsyncCancel();
}
// Clean up.
message.Dispose();
Console.WriteLine("Goodbye.");
}
}
}
Just because this is a little vague...I will be brief...
There are a lot of ways to do asynchronous or parallel work in c#/.net etc.
The fastest way to do what you want is to use a background worker thread which will avoid locking up your UI.
A tip with background worker threads : you cannot directly update the UI from them (thread affinity and Marshalling is just something you learn to deal with...)
Another thing to consider...if you use the standard System.Net.Mail type stuff to send the emails...be careful how you craft your logic. If you isolate it all in some method and call it over and over, it will likely have to tear down and rebuild the connection to the mail server each time and the latency involved in authentication etc will still slow the whole thing down unnecessarily. Send multiple e-mails through a single open connection to the mail server when possible.
Here is a fire and forget approach together with async using .Net 4.5.2+:
BackgroundTaskRunner.FireAndForgetTaskAsync(async () =>
{
SmtpClient smtpClient = new SmtpClient(); // using configuration file settings
MailMessage message = new MailMessage(); // TODO: Initialize appropriately
await smtpClient.SendMailAsync(message);
});
where BackgroundTaskRunner is:
public static class BackgroundTaskRunner
{
public static void FireAndForgetTask(Action action)
{
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2+ required
{
try
{
action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
/// <summary>
/// Using async
/// </summary>
public static void FireAndForgetTaskAsync(Func<Task> action)
{
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2+ required
{
try
{
await action();
}
catch (Exception e)
{
// TODO: handle exception
}
});
}
}
Works like a charm on Azure App Services.
Try this:
var client = new System.Net.Mail.SmtpClient("smtp.server");
var message = new System.Net.Mail.MailMessage() { /* provide its properties */ };
client.SendAsync(message, null);
What you want to do is run the e-mail task on a separate thread so the main code can continue processing while the other thread does the e-mail work.
Here is a tutorial on how to do that:
Threading Tutorial C#
Use the SmtpClient class and use the method SendAsync in the System.Net.Mail namespace.
Using the Task Parallel Library in .NET 4.0, you can do:
Parllel.Invoke(() => { YourSendMailMethod(); });
Also, see cristina manu's blog post about Parallel.Invoke() vs. explicit task management.
i think this is the best way :
public async Task Send(string to, string subject, string body)
{
MailMessage mail = new MailMessage();
SmtpClient SmtpServer = new SmtpClient("smtp.mail.yahoo.com");
mail.From = new MailAddress("yourEmail#email.com", "sender name");
mail.To.Add(to);
mail.Subject = subject;
mail.Body = body;
mail.IsBodyHtml = true;
SmtpServer.Port = 587;
SmtpServer.Credentials = new System.Net.NetworkCredential("yourEmail#email.com", "your key");
SmtpServer.EnableSsl = true;
await Task.Run(() =>
{
SmtpServer.SendAsync(mail, null);
});
}

Sending e-mail from a Custom SQL Server Reporting Services Delivery Extension

I've developed my own delivery extension for Reporting Services 2005, to integrate this with our SaaS marketing solution.
It takes the subscription, and takes a snapshot of the report with a custom set of parameters. It then renders the report, sends an e-mail with a link and the report attached as XLS.
Everything works fine, until mail delivery...
Here's my code for sending e-mail:
public static List<string> SendMail(SubscriptionData data, Stream reportStream, string reportName, string smptServerHostname, int smtpServerPort)
{
List<string> failedRecipients = new List<string>();
MailMessage emailMessage = new MailMessage(data.ReplyTo, data.To);
emailMessage.Priority = data.Priority;
emailMessage.Subject = data.Subject;
emailMessage.IsBodyHtml = false;
emailMessage.Body = data.Comment;
if (reportStream != null)
{
Attachment reportAttachment = new Attachment(reportStream, reportName);
emailMessage.Attachments.Add(reportAttachment);
reportStream.Dispose();
}
try
{
SmtpClient smtp = new SmtpClient(smptServerHostname, smtpServerPort);
// Send the MailMessage
smtp.Send(emailMessage);
}
catch (SmtpFailedRecipientsException ex)
{
// Delivery failed for the recipient. Add the e-mail address to the failedRecipients List
failedRecipients.Add(ex.FailedRecipient);
}
catch (SmtpFailedRecipientException ex)
{
// Delivery failed for the recipient. Add the e-mail address to the failedRecipients List
failedRecipients.Add(ex.FailedRecipient);
}
catch (SmtpException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
// Return the List of failed recipient e-mail addresses, so the client can maintain its list.
return failedRecipients;
}
Values for SmtpServerHostname is localhost, and port is 25.
I veryfied that I can actually send mail, by using Telnet. And it works.
Here's the error message I get from SSRS:
ReportingServicesService!notification!4!08/28/2008-11:26:17:: Notification 6ab32b8d-296e-47a2-8d96-09e81222985c completed. Success: False, Status: Exception Message: Failure sending mail. Stacktrace: at MyDeliveryExtension.MailDelivery.SendMail(SubscriptionData data, Stream reportStream, String reportName, String smptServerHostname, Int32 smtpServerPort) in C:\inetpub\wwwroot\CustomReporting\MyDeliveryExtension\MailDelivery.cs:line 48
at MyDeliveryExtension.MyDelivery.Deliver(Notification notification) in C:\inetpub\wwwroot\CustomReporting\MyDeliveryExtension\MyDelivery.cs:line 153, DeliveryExtension: My Delivery, Report: Clicks Development, Attempt 1
ReportingServicesService!dbpolling!4!08/28/2008-11:26:17:: NotificationPolling finished processing item 6ab32b8d-296e-47a2-8d96-09e81222985c
Could this have something to do with Trust/Code Access Security?
My delivery extension is granted full trust in rssrvpolicy.config:
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="MyDelivery_CodeGroup"
Description="Code group for MyDelivery extension">
<IMembershipCondition class="UrlMembershipCondition" version="1" Url="C:\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\bin\MyDeliveryExtension.dll" />
</CodeGroup>
Could trust be an issue here?
Another theory: SQL Server and SSRS was installed in the security context of Local System. Am I right, or is this service account restricted access to any network resource? Even its own SMTP Server?
I tried changing all SQL Server Services logons to Administrator - but still without any success.
I also tried logging onto the SMTP server in my code, by proviiding: NetworkCredential("Administrator", "password") and also NetworkCredential("Administrator", "password", "MyRepServer")
Can anyone help here, please?
What's at:
at MyDeliveryExtension.MailDelivery.SendMail(SubscriptionData data, Stream reportStream, String reportName, String smptServerHostname, Int32 smtpServerPort)
in C:\inetpub\wwwroot\CustomReporting\MyDeliveryExtension\MailDelivery.cs:line 48
at MyDeliveryExtension.MyDelivery.Deliver(Notification notification)
in C:\inetpub\wwwroot\CustomReporting\MyDeliveryExtension\MyDelivery.cs:line 153
Also you seem to be disposing the report stream, but that should be done by whatever opened that stream, not your method (it won't be obvious that attaching a stream disposes it).
You're losing part of your stack trace due to how you re-throw exceptions. Don't throw the ex variable, just throw is enough.
Try this tweak:
public static List<string> SendMail(SubscriptionData data, Stream reportStream, string reportName, string smptServerHostname, int smtpServerPort)
{
List<string> failedRecipients = new List<string>();
MailMessage emailMessage = new MailMessage(data.ReplyTo, data.To) {
Priority = data.Priority,
Subject = data.Subject,
IsBodyHtml = false,
Body = data.Comment
};
if (reportStream != null)
emailMessage.Attachments.Add(new Attachment(reportStream, reportName));
try
{
SmtpClient smtp = new SmtpClient(smptServerHostname, smtpServerPort);
// Send the MailMessage
smtp.Send(emailMessage);
}
catch (SmtpFailedRecipientsException ex)
{
// Delivery failed for the recipient. Add the e-mail address to the failedRecipients List
failedRecipients.Add(ex.FailedRecipient);
//are you missing a loop here? only one failed address will ever be returned
}
catch (SmtpFailedRecipientException ex)
{
// Delivery failed for the recipient. Add the e-mail address to the failedRecipients List
failedRecipients.Add(ex.FailedRecipient);
}
// Return the List of failed recipient e-mail addresses, so the client can maintain its list.
return failedRecipients;
}
I tried to remove the reportStream Attachment:
//if (reportStream != null)
//emailMessage.Attachments.Add(new Attachment(reportStream, reportName));
And now it works fine.
So it is something to do with the reportStream.
After fooling around with the tunctionallity that gets the reportStream, I was able to fix the mail sending problem.
The error wasn't in the SendMail method, but somewehere else. The exception was thrown in the context, of SendMail though. Buggered!
That's why you have to avoid:
catch (Exception ex)
{
throw ex;
}
As that basically cloaks your exception in a new one.
If you use:
catch (Exception ex)
{
throw; //note: no ex
}
It keeps the original exception and stack trace.
FileStream m_fileStream = null;
m_files = notification.Report.Render(format, null);
RenderedOutputFile m_renderedOutputFile = m_files[0];
m_fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write);
m_renderedOutputFile.Data.Seek((long)0, SeekOrigin.Begin);
byte[] arr = new byte[(int)m_renderedOutputFile.Data.Length + 1];
m_renderedOutputFile.Data.Read(arr, 0, (int)m_renderedOutputFile.Data.Length);
m_fileStream.Write(arr, 0, (int)m_renderedOutputFile.Data.Length);
m_fileStream.Close();

Categories

Resources