Deleting MailMessage attachments causes website to hang - c#

I am attempting to send an email asynchronously that has attachments. These attachments are generated on the fly and saved to the server, so I would like to delete them after the email has been sent.
The code below is successful in sending the email, however the whole site locks up afterwards. I can only assume that it cannot delete the generated file that has been attached to the email, no errors are thrown and no logs are posted. I know the file path is correct, as the same path is being used to attach the file to the email. Note I am disposing the Message and Attachment objects.
Everything works fine in my development environment, but not on production. It should be noted that this code is being executed from a .dll that is placed in the bin folder of the website.
ThreadStart starter = delegate { SendAsync(ref message, Attachments, Settings); };
Thread thread = new Thread(starter);
thread.IsBackground = true;
thread.Start();
private static void SendAsync(ref MailMessage Message, List<clsEmailAttachment> Attachments, clsSmtpSettings Settings)
{
try
{
SmtpClient smtp = new SmtpClient();
smtp.Host = Settings.Host;
NetworkCredential Credential = new NetworkCredential(Settings.Username, Settings.Password, Settings.Domain);
smtp.UseDefaultCredentials = false;
smtp.Credentials = Credential;
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.Send(Message);
}
catch (Exception SendingEmailException)
{
Log.PostLog(null, 4007, SendingEmailException.Message);
}
finally
{
try
{
Message.Attachments.Dispose();
Message.Dispose();
foreach (clsEmailAttachment attachment in Attachments)
{
if (attachment.Delete == true)
{
try
{
File.Delete(attachment.Path);
}
catch { throw; }
}
}
}
catch (Exception DeletingAttachmentsException)
{
Log.PostLog(null, 4008, DeletingAttachmentsException.Message);
}
}
}

After much trial and error I have solved the problem. It turns out to be a multi-threading issue with the Attachments collection. The following is being called on a new thread:
private static void SendAsync(ref MailMessage Message, List<clsEmailAttachment> Attachments, clsSmtpSettings Settings)
Generic.List<type> is not a threadsafe collection. The following statement is taken from the msdn:
The collection classes introduced in the .NET Framework 2.0 are found in the System.Collections.Generic namespace. These include List, Dictionary, and so on. These classes provide improved type safety and performance compared to the .NET Framework 1.0 classes. However, the .NET Framework 2.0 collection classes do not provide any thread synchronization; user code must provide all synchronization when items are added or removed on multiple threads concurrently.
See here for the Full article

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 understand C# SendAsync function?

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.

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.

Sending mail periodically in ASP.net

I'm trying to send confirmation mails to users periodically in ASP.NET.
To do this I polulate a queue with mails and check it every 30 seconds. Any confirmation emails in the queue at this time are sent and then cleared from the queue.
Does anyone know how to do this?
Here is my sending mail code
public static bool SendMail(string AdminMail,string AdminPassword,string subject,string toAddress, string content,DateTime SendTime)
{
toAddressListProperty.Enqueue(toAddress);
if(date==null)
{
date = DateTime.Now.Second;
}
if (date-SendTime.Second > 120)
{
var message = new MailMessage
{
From = new MailAddress(AdminMail)
};
foreach (var toAddressl in toAddressListProperty)
{
message.To.Add(new MailAddress(toAddressl));
}
message.Subject = subject;
message.Body = content;
message.IsBodyHtml = true;
var smtp = new SmtpClient
{
Credentials = new System.Net.NetworkCredential(AdminMail, AdminPassword),
Port = 587,
Host = "smtp.gmail.com",
EnableSsl = true
};
smtp.Send(message);
//date = SendTime;
return true;
}
return false;
}
I have done this using a background thread. I did a little research, and I believe this is an ok approach. There are a few dangers, which this blog details.
The main thing is to ensure you never throw an exception from a background thread, as I believe that will cause the web process to restart. Also, incase the thread dies, I ensure it is running on every call.
I have been using this approach for a few months, and so far no issues.
Also I run it every 1 second, this minamizes the amount of time you might loose emails due to an app shutdown.
public class BackgroundSmtpService
{
private ILog _log = LogManager.GetLogger(typeof(BackgroundSmtpService));
private readonly SmtpService SmtpService;
private static Thread _watchThread;
private static List<Email> _emailToSend = new List<Email>();
public BackgroundSmtpService(SmtpService smtpService)
{
SmtpService = smtpService;
}
public void Send(Email email)
{
lock (_emailToSend)
{
_emailToSend.Add(email);
}
EnsureRunning();
}
private void EnsureRunning()
{
if (_watchThread == null || !_watchThread.IsAlive)
{
lock (SmtpService)
{
if (_watchThread == null || !_watchThread.IsAlive)
{
_watchThread = new Thread(ThreadStart);
_watchThread.Start();
}
}
}
}
private void ThreadStart()
{
try
{
while (true)
{
Thread.Sleep(1000);
try
{
lock (_emailToSend)
{
var emails = _emailToSend;
_emailToSend = new List<Email>();
emails.AsParallel().ForAll(a=>SmtpService.Send(a));
}
}
catch (Exception e)
{
_log.Error("Error during running send emails", e);
}
}
}
catch (Exception e)
{
_log.Error("Error during running send emails, outer", e);
}
}
}
You might want to consider using Quartz.net library. It have decent documentation and it's fairly easy to use.
The biggest challenge you'll have with this is that any time your application pool recycles it will take a new request to kick stats your "timer". If you had an HTTP monitor application such as Pingdom to poll your server it shouldn't be a problem, but then again you could also just use a third party monitor tool to hit your a page on your site every N seconds that would send out the mail and issue a response.
I myself would use a Windows service to pull a queue from a database and send out messages that way.
Easiest way is to create a VBScript that sends an HTTP GET request to http://localhost/SendConfirmationEmails.aspx
You'd start the VBScript in your global.asax Application_Start method.
The SendConfirmationEmails.aspx would act as a simple web service (you could use an ashx, or actual web service asmx if you wanted). It would only be accessible on the localhost so remote users wouldn't be able to spam it.
Using a windows service is probably the best practice method, but a simple VBScript will get the job done.
surl="http://localhost/SendConfirmationEmails.aspx"
set oxmlhttp=createobject("msxml2.xmlhttp")
with oxmlhttp
.open "GET",surl,false
.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
.send srequest
end with
You'd put the code above in a while wend loop with a Sleep to delay every 30 seconds...

Set a background process and allow the user to continue without waiting

I have a newsletter tool that I am trying to setup to run as a background process to send out the emails. The code below works without any issues but the problem I have is that it is slow.
If there are 50 emails to send it can be very slow for the end user as they have to stare at the screen for up to 1min 30secs. This becomes a bigger problem for me if they client is sending an email to a larger group of people.
The reason I send each mail individually as apposed to sending 1 and bcc'ing the email list is that each email contains certain specific content for each user - like unsubscribe link codes, personal name at the start of the mail, etc.
I am looking for a solution where I can let the user click on a button and have .net run the sending email part in the background while the front end user is brought to a page saying that their email is being sent. Ideally, it should take no longer than a regular postback for all that to occur - not the current few minutes.
Any thoughts on how best to achieve this?
Thanks for your help,
Rich
if (Page.IsPostBack)
{
if (JustMeButton.Checked)
{
SendMail("emailme#address", EmailTemplate);
}
if (EveryoneButton.Checked)
{
//setup background process
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = false;
bw.WorkerSupportsCancellation = false;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
//bring user to next screen and display message
Response.Redirect("emailSendingMessageScreen.aspx");
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
DataTable emailTable = (DataTable)Session["emailTable"];
foreach (DataRow row in emailTable.Rows)
{
SendMail(row["email"], row["name"], EmailTemplate);
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!(e.Error == null))
{
SendMail("admin#address", "Error sending <br><br>" + e.Error.Message);
}
else
{
SendMail("admin#address", "emails sent successfully.");
}
//clear out the sessions created for sending this email
Session.Remove("emailTable");
}
private void SendMail(string email, string emailMessage)
{
MailMessage mailMessage = new MailMessage();
mailMessage.From = new MailAddress("from#address");
mailMessage.To.Add(new MailAddress(email));
mailMessage.Subject = Server.HtmlEncode(EmailSubject.Text.Trim());
mailMessage.Body = emailMessage;
mailMessage.IsBodyHtml = true;
SmtpClient smtpClient = new SmtpClient();
Object userState = mailMessage;
smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
smtpClient.Timeout = 10000;
try
{
smtpClient.SendAsync(mailMessage, userState);
}
catch (SmtpException smtpExc)
{
MailMessageTxt.Text += "Error Code: " + smtpExc.StatusCode;
MailMessageTxt.Visible = true;
}
catch (Exception ex)
{
MailMessageTxt.Text += "Error is: " + ex;
MailMessageTxt.Visible = true;
}
}
void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
MailMessage mailMessage = e.UserState as MailMessage;
if (e.Error != null)
{
MailMessageTxt.Text = "Error occured, info=" + e.Error.Message;
MailMessageTxt.Visible = true;
}
}
I did this very thing sending a newsletter with the BeerHouse in the new version. You can get the book now and the source code is on CodePlex, http://thebeerhouse.codeplex.com/
http://professionalaspnet.com/archive/2009/10/07/ASP.NET-3.5-Problem-1320-Design-2D00-Solution.aspx
alt text http://Professionalaspnet.com/images/187586-fg0705.jpg
The Solution uses AJAX to send the e-mails and allows the user to keep browsing around the site without being concerned about the newsletter being sent out. When it is done it just takes care of itself and the user can check on as they want. Its chapter 7 in the book, enjoy.
A thread created within an ASP page will get killed if the ASP worker process is recycled for whatever reason. A Windows service that performs the task via a message queue is ideal for long running jobs. Another "trick" solution is using cache expiry, explained here: http://www.codeproject.com/KB/aspnet/ASPNETService.aspx
Move all the work of sending the email to separate class and run it using ThreadPool
MailSender sender = new MailSender(parameters. ....);
ThreadPool.EnqueueUserItem(sender.sendAllEmails)
Using background worker won't work. It will be disposed when it goes out of context, meaning on Response.End
I have found trying to do tasks like this within the ASP.NET process is problematic as you cannot guarantee the process will complete or be successful. If the process gets cut off you have no recovery. I would have all your emails saved to a DB first and then have a service that polls for new entries in this database or table that handles the actual sending of the emails.
The advantage to this is that if your email provider or ASP.NET process goes down you don't lose any emails and you have a history of all emails sent with their details of when, who, etc... You can also rip out or change the emailer to do more, like send to Twitter or a phone text message etc. This effectively decouples your notifications from your application.
All applications I have made recently use this type of model and it has stopped emails from being lost due to service failures and other reasons. It has also made it possible to lookup all emails that have gone through the system and get metrics that allows me to optimize the need to send emails by storing extra information in the email record like reason sent, if it's to report an error, etc... Adding on additions such as routing notifications (eg go to text message instead if email) based on time of day or user has been possible with no changes to the primary applicaton.
Simply use ajax to execute the process.let the user continue their activity while the server bares the burden.

Categories

Resources