Using a button to sent mail async and disposing of it in the task itself. Is this the correct way to do it?
Or do I need a catch inside the task and do I need a finally dispose on the button click?
And do I need to dispose of other things like the smtpclient? The mails don't have any attachments they are all just plain text.
private async void closeorder.Click(object sender, EventArgs e)
{
try
{
await SendEmail();
}
catch (exception)
{
MessageBox.Show("mail has not been sent");
}
}
private async Task SendEmail()
{
SmtpClient smtpClient = new SmtpClient();
MailMessage message = new MailMessage();
if (System.IO.File.Exists(emailfile)
{
String[] Lines = System.IO.File.ReadAllLines(emailfile);
foreach (string line in Lines)
message.To.Add(line);
}
else
{
System.IO.File.WriteAllText(emailfile,"");
TextWriter Email = new StreamWriter(emailfile, true);
Email.Writeline("foo#email.com");
Email.close();
}
try
{
MailAdress fromAddress = new MailAddress("goo#email.com","order");
smtpClient.Host "";
smtpClient.Port xx;
smtpClient.credentials = ("""");
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
message.From = fromAddress;
message.Subject = "";
message.IsBodyHtml = True;
message.Body = Mybody();
message.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
await smtpClient.SendMailAsync(message);
}
finally
{
message.Dispose();
}
}
Sorry if formatting isn't the best typed on mobile.
Both SmtpClient and MailMessage implement the IDisposable interface so you should dispose them.
https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient?view=net-6.0
https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.mailmessage?view=net-6.0
The fact it is in a asynchronous flow does not invalidate the fact you need to dispose properly those objects.
But you can avoid to use the try/catch/finally with the using keyword:
using(var smtpClient = new SmtpClient())
using(var message = new MailMessage())
{
// Your code
}
It will generate for you (behing the scene) the try/catch/finally blocks and the call to dispose.
Related
I have a loop where if you dropped a file into a folder, it would email you on success/failure. I tried the 'using' statement which implements Dispose() automatically.
I also tried a 'try/finally' statement (recommended here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement . Both of which gave me this error:
Cannot access a disposed object. Object name 'system.net.mail.smtpclient'
Attempt 1:
private int _disposed;
private readonly string _fromEmail;
private readonly SmtpClient _client;
public Emailer()
{
_disposed = 0;
_fromEmail = "email";
_client = new SmtpClient("domain-com.mail.protection.outlook.com", 25)
{
EnableSsl = true,
Credentials = new NetworkCredential(_fromEmail, "password")
};
}
public async Task SendEmailAsync(IList<string> to, string subject, string body)
{
using (MailMessage mail = new MailMessage())
{
mail.From = new MailAddress(_fromEmail);
foreach (string toEmail in to)
mail.To.Add(toEmail);
mail.Subject = subject;
mail.Body = body;
mail.IsBodyHtml = true;
using (SmtpClient smtp = _client)
{
smtp.Credentials = _client.Credentials;
smtp.EnableSsl = _client.EnableSsl;
await smtp.SendMailAsync(mail);
}
}
}
Attempt 2:
private int _disposed;
private readonly string _fromEmail;
private readonly SmtpClient _client;
public Emailer()
{
_disposed = 0;
_fromEmail = "email";
_client = new SmtpClient("domain-com.mail.protection.outlook.com", 25)
{
EnableSsl = true,
Credentials = new NetworkCredential(_fromEmail, "password")
};
}
public async Task SendEmailAsync(IList<string> to, string subject, string body)
{
using (MailMessage mail = new MailMessage())
{
mail.From = new MailAddress(_fromEmail);
foreach (string toEmail in to)
mail.To.Add(toEmail);
mail.Subject = subject;
mail.Body = body;
mail.IsBodyHtml = true;
try
{
SmtpClient smtp = _client;
smtp.Credentials = _client.Credentials;
smtp.EnableSsl = _client.EnableSsl;
await smtp.SendMailAsync(mail);
}
finally
{
Dispose();
}
}
}
My Dispose() Method
public void Dispose()
{
if (Interlocked.CompareExchange(ref _disposed, 1, 0) != 0)
return;
_client.Dispose();
}
}
}
You are disposing a Smtp object on your first call. You need to expose method to dispose smtp object and once your loop gets complete call this method to dispose your smtp object and get rid of your using statement.
public void DisposeSMTPClient()
{
_client.Dispose();
}
public async Task SendEmailAsync(IList<string> to, string subject, string body)
{
var mail = new MailMessage();
mail.From = new MailAddress(_fromEmail);
foreach (string toEmail in to)
mail.To.Add(toEmail);
mail.Subject = subject;
mail.Body = body;
mail.IsBodyHtml = true;
await _client.SendMailAsync(mail);
}
Call DisposeSMTPClient this function after your loops end
My problem was using 2 using statements.
I fixed it by using a try catch block for sending the email and the original using statement along with implementing the IDisposable class.
using (MailMessage mail = new MailMessage())
{
mail.From = new MailAddress(_fromEmail);
foreach (string toEmail in to)
mail.To.Add(toEmail);
mail.Subject = subject;
mail.Body = body;
mail.IsBodyHtml = true;
//mail.Attachments.Add(new Attachment("D:\\TestFile.txt"));//--Uncomment this to send any attachment
try
{
SmtpClient smtp = _client;
smtp.Credentials = _client.Credentials;
smtp.EnableSsl = _client.EnableSsl;
await smtp.SendMailAsync(mail);
}
catch
{ }
}
I am using a service component through ASP.NET MVC.
I would like to send the email in a asynchronous way to let the user do other stuff without having to wait for the sending.
When I send a message without attachments it works fine.
When I send a message with at least one in-memory attachment it fails.
So, I would like to know if it is possible to use an async method with in-memory attachments.
Here is the sending method
public static void Send() {
MailMessage message = new MailMessage("from#foo.com", "too#foo.com");
using (MemoryStream stream = new MemoryStream(new byte[64000])) {
Attachment attachment = new Attachment(stream, "my attachment");
message.Attachments.Add(attachment);
message.Body = "This is an async test.";
SmtpClient smtp = new SmtpClient("localhost");
smtp.Credentials = new NetworkCredential("foo", "bar");
smtp.SendAsync(message, null);
}
}
Here is my current error
System.Net.Mail.SmtpException: Failure sending mail.
---> System.NotSupportedException: Stream does not support reading.
at System.Net.Mime.MimeBasePart.EndSend(IAsyncResult asyncResult)
at System.Net.Mail.Message.EndSend(IAsyncResult asyncResult)
at System.Net.Mail.SmtpClient.SendMessageCallback(IAsyncResult result)
--- End of inner exception stack trace ---
Solution
public static void Send()
{
MailMessage message = new MailMessage("from#foo.com", "to#foo.com");
MemoryStream stream = new MemoryStream(new byte[64000]);
Attachment attachment = new Attachment(stream, "my attachment");
message.Attachments.Add(attachment);
message.Body = "This is an async test.";
SmtpClient smtp = new SmtpClient("localhost");
//smtp.Credentials = new NetworkCredential("login", "password");
smtp.SendCompleted += delegate(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
System.Diagnostics.Trace.TraceError(e.Error.ToString());
}
MailMessage userMessage = e.UserState as MailMessage;
if (userMessage != null)
{
userMessage.Dispose();
}
};
smtp.SendAsync(message, message);
}
Don't use "using" here. You are destroying the memory stream immediately after calling SendAsync, e.g. probably before SMTP gets to read it (since it's async). Destroy your stream in the callback.
I have tried your function and it works even for email with in memory attachments. But here are some remarks:
What type of attachments did you try to send ? Exe ?
Are both sender and receiver on the same email server ?
You should “catch” exception and not just swallow it, than you will get more info about your problem.
What does the exception says ?
Does it work whan you use Send instead of SendAsync ? You are using 'using' clause and closing Stream before email is sent.
Here is good text about this topic:
Sending Mail in .NET 2.0
An extension to the Solution supplied in the original question also correctly cleans up an attachments that may also require disposal.
public event EventHandler EmailSendCancelled = delegate { };
public event EventHandler EmailSendFailure = delegate { };
public event EventHandler EmailSendSuccess = delegate { };
...
MemoryStream mem = new MemoryStream();
try
{
thisReport.ExportToPdf(mem);
// Create a new attachment and put the PDF report into it.
mem.Seek(0, System.IO.SeekOrigin.Begin);
//Attachment att = new Attachment(mem, "MyOutputFileName.pdf", "application/pdf");
Attachment messageAttachment = new Attachment(mem, thisReportName, "application/pdf");
// Create a new message and attach the PDF report to it.
MailMessage message = new MailMessage();
message.Attachments.Add(messageAttachment);
// Specify sender and recipient options for the e-mail message.
message.From = new MailAddress(NOES.Properties.Settings.Default.FromEmailAddress, NOES.Properties.Settings.Default.FromEmailName);
message.To.Add(new MailAddress(toEmailAddress, NOES.Properties.Settings.Default.ToEmailName));
// Specify other e-mail options.
//mail.Subject = thisReport.ExportOptions.Email.Subject;
message.Subject = subject;
message.Body = body;
// Send the e-mail message via the specified SMTP server.
SmtpClient smtp = new SmtpClient();
smtp.SendCompleted += SmtpSendCompleted;
smtp.SendAsync(message, message);
}
catch (Exception)
{
if (mem != null)
{
mem.Dispose();
mem.Close();
}
throw;
}
}
private void SmtpSendCompleted(object sender, AsyncCompletedEventArgs e)
{
var message = e.UserState as MailMessage;
if (message != null)
{
foreach (var attachment in message.Attachments)
{
if (attachment != null)
{
attachment.Dispose();
}
}
message.Dispose();
}
if (e.Cancelled)
EmailSendCancelled?.Invoke(this, EventArgs.Empty);
else if (e.Error != null)
{
EmailSendFailure?.Invoke(this, EventArgs.Empty);
throw e.Error;
}
else
EmailSendSuccess?.Invoke(this, EventArgs.Empty);
}
when I send email using STMP in C#, I can't make sure that the email has been send although I have used SendCompleted Event Handler. this is my code:
private void btnLogin1_Click(object sender, EventArgs e)
{
try
{
MailAddress FromAddress = new MailAddress("*******", "*******");
MailAddress ToAddress = new MailAddress("*****");
String FromPassword = "******";
SmtpClient SMTP = new SmtpClient();
SMTP.Host = "smtp.yandex.com";
SMTP.Port = 587;
SMTP.EnableSsl = true;
SMTP.DeliveryMethod = SmtpDeliveryMethod.Network;
SMTP.UseDefaultCredentials = false;
SMTP.Credentials = new NetworkCredential(FromAddress.Address, FromPassword);
SMTP.SendCompleted += SMTP_SendCompleted;
MailMessage Message = new MailMessage();
Message.From = FromAddress;
Message.To.Add(ToAddress);
Message.Subject = "Welcome";
Message.SubjectEncoding = Encoding.UTF8;
Message.Priority = MailPriority.High;
Message.IsBodyHtml = true;
Message.Body = "<b>Test Mail</b>";
Message.BodyEncoding = Encoding.UTF8;
SMTP.Send(Message);
}
catch { }
}
private void SMTP_SendCompleted(object sender, AsyncCompletedEventArgs e)
{
MessageBox.Show("Sent");
}
}
Thanks in advance.
You need to put some error handling code inside
Catch {...}
Otherwise, you are just catching an error and ignoring it.
If the catch block doesn't fire, then as far as you can be reasonably sure (without checking for a delivery receipt), then you can assume the email has been successfully sent.
If you are using the SendCompleted event, then the email needs sent asynchronously using SendAsync. Otherwise, the method will not return control until after the email is either sent or fails.
In the example you posted, the empty catch is swallowing up any errors that may be occurring to even determine that much.
So either use the SendAsync so that your event gets fired, or actually see if any exceptions are being caught. Empty catch blocks that aren't even catching any specific exception are terrible ideas in nearly every situation. They lead to all sorts of problems.
I need to send list box items to email. Code is running fine, but when it gets to Send method it fails to send it.
protected void ProcessButton_Click(object sender, EventArgs e)
{
try
{
MailMessage mailMessage = new MailMessage();
mailMessage.To.Add("someone#live.com");
mailMessage.From = new MailAddress("myAdress#live.com");
mailMessage.Subject = "ASP.NET e-mail test";
mailMessage.Body = OrderListBox.Items.ToString();
SmtpClient smtpClient = new SmtpClient("smtp.live.com");
smtpClient.Send(mailMessage);
Response.Write("E-mail sent!");
}
catch (Exception ex)
{
Response.Write("Could not send email - error: " + ex.Message);
}
}
You can write list in file and send it as attachment (gmail example):
protected bool ProcessButton_Click(object sender, EventArgs e)
{
SmtpClient client = new SmtpClient();
client.Host = "smtp.gmail.com";
client.Port = 587;
client.Credentials = new NetworkCredential("myEmail#gmail.com", "password");
//if you have double verification on gmail, then generate and write App Password
client.EnableSsl = true;
MailMessage message = new MailMessage(new MailAddress("myEmail#gmail.com"),
new MailAddress(receiverEmail));
message.Subject = "Title";
message.Body = $"Text";
// Attach file
Attachment attachment;
attachment = new Attachment("D:\list.txt");
message.Attachments.Add(attachment);
try
{
client.Send(message);
// ALL OK
return true;
}
catch
{
//Have problem
return false;
}
}
and write this on begining of code:
using System.Net;
using System.Net.Mail;
You can choose smpt host adress and port if you want.
I was wondering if theres a way to send email asynchronously in asp.net c#. I've used the following code to send the emails:
if (checkObavijest.Checked)
{
List<hsp_Kupci_Newsletter_Result> lista = ServisnaKlasa.KupciNewsletter();
for (int i = 0; i < lista.Count; i++)
{
MailMessage mail = new MailMessage();
mail.From = new MailAddress("*******");
mail.To.Add(lista[i].Email);
mail.Subject = "Subject";
mail.SubjectEncoding = Encoding.UTF8;
mail.BodyEncoding = Encoding.UTF8;
mail.IsBodyHtml = true;
mail.Priority = MailPriority.High;
mail.Body = "Some message";
SmtpClient smtpClient = new SmtpClient();
Object state = mail;
smtpClient.Credentials = new NetworkCredential("****#gmail.com", "****");
smtpClient.Port = 587;
smtpClient.Host = "smtp.gmail.com";
smtpClient.EnableSsl = true;
smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
try
{
smtpClient.SendAsync(mail, state);
}
catch (Exception ex)
{
}
}
void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
MailMessage mail = e.UserState as MailMessage;
if (!e.Cancelled && e.Error != null)
{
string poruka = "Emailovi poslani!";
ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + poruka + "');", true);
}
}
But when I tick the option for sending emails, the page loads like 40 seconds, then after 40 seconds emails are sent then. As you can see I'm pulling out like 20-30 emails currently out of my DB. I thought this was the correct way to send mails asynchronously, but apparently its not... How can I push the email sending to another thread so it doesn't affects the user who is using this page? Can someone help me out with this ? Thanks !
The reason is mentioned here on the MSDN documentation on SmtpClient.SendAsync
After calling SendAsync, you must wait for the e-mail transmission to
complete before attempting to send another e-mail message using Send
or SendAsync.
You could call SendAsync using ThreadPool thru a wrapped method call. This would give you something like a fire-and-forget scenario.
Something like this:
public void SendViaThread(System.Net.Mail.MailMessage message) {
try {
System.Threading.ThreadPool.QueueUserWorkItem(SendViaAsync, message);
} catch (Exception ex) {
throw;
}
}
private void SendViaAsync(object stateObject) {
System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient();
System.Net.Mail.MmailMessage message = (MmailMessage)stateObject;
...
smtp.Credentials = new NetworkCredential("...");
smtp.Port = 587;
smtp.Host = "...";
...
smtp.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
...
smtp.Send(message);
}
And you would call SendViaThread(mail) in your code. So, your loop now becomes:
for (int i = 0; i < lista.Count; i++) {
MailMessage mail = new MailMessage();
mail.From = new MailAddress("...");
mail.To.Add(lista[i].Email);
mail.Subject = "...";
...
mail.Body = "...";
SendViaThread(mail);
}
If you want to not have your main thread waiting, try tossing it in a Task.
Task.Factory.StartNew( () => {
// do mail stuff here
});
Be aware, though that each time you spawn a task, it'll spawn a new (or re-use) a thread that your system has made. If you're firing off 30 e-mails, you have the potential of firing off a lot of threads (the system has a programmable cap, too). Task.Factory.StartNew is a very simple way to do a fire-and-forget process.
http://msdn.microsoft.com/en-us/library/dd321439%28v=vs.110%29.aspx
(in this example, the code is also keeping a collection of Tasks, which is nice if you care to manage them -- but in reality, you only need the Task.Factory.StartNew(()=>{ bit if you want to fire-and-forget. Just be careful because you don't want to have orphaned threads pegging your memory usage.