Sending mails asynchronously - asp.net

I have method as below to send email, when ever any error occurs in our application. [Yes. sending errors through mails is a separate discussion]
public void EmailErrorDetails()
{
string strBodyMessage = string.Empty;
strBodyMessage = GetEmailBodyMessage();
if (strBodyMessage != String.Empty)
{
MailMessage emailMessage = new MailMessage();
emailMessage.From = new MailAddress(Constants.MailFrom);
emailMessage.To.Add(Constants.MailTo);
emailMessage.Subject = Constants.EmailSubject;
emailMessage.IsBodyHtml = true;
emailMessage.Body = string.Format(strBodyMessage);
SmtpClient client = new SmtpClient();
client.UseDefaultCredentials = false;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.EnableSsl = true;
client.Host = Constants.EmailHostAddress;
client.Port = Convert.ToInt32(Constants.EmailPort);
client.Credentials = new NetworkCredential(Constants.MailFrom, Constants.MailFromPassword);
client.Send(emailMessage);
}
}
I want to make this method run asynchronously in background, and the execution to move ahead.
I read that [client.Send] itself uses asynchronous calling. So is there any benefit making the above method call asynchronous way, and if yes how can I achieve this?

I read that client.Send uses asynchronous calling
Assuming client is actually an SmtpClient, then Send does not use asynchronous calling. SmtpClient has a specific method for sending emails asynchronously and it's called SendAsync - if you want to send an email in the background, use this instead of Send.

Related

Sent an email from asp.net through 365 with a "from" email that is different than the username

I hope someone can help me out here:
Users generate emails through my asp.net website. Emails are to be sent out through the 365 server using an account that I have there with my domain (e.g. out#mydomain.com). I want the message "from" field to be the email of my user (e.g., myuser#anotherdomain.com) so that the receivers of the email will see his email as the sender and will reply to him directly.
But when I try this I get an error message (see below).
I get the same error message even if I try to send an email when the "from" field is another existing mailbox in my domain (e.g., myname#mydomain.com)
Apparently someone in the way (not sure if the asp.net or the 365 server) blocks emails if the "from" is not identical to the username.
Is there any way to address this>
here is my code
protected void btnSend_Click(object sender, System.EventArgs e)
{
AuditLog.Info("here");
try
{
string EmailContent = "test";
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;
msg.From = new MailAddress("myuser#anotherdomain.com");
msg.Bcc.Add(msg.From);
string email = "receiver#gmail.com";
msg.To.Add(email);
msg.Subject = "TEst 365";
System.Net.Mail.AlternateView plainTextView = System.Net.Mail.AlternateView.CreateAlternateViewFromString(EmailContent);
System.Net.Mail.AlternateView htmlView = System.Net.Mail.AlternateView.CreateAlternateViewFromString(EmailContent);
msg.AlternateViews.Add(plainTextView);
msg.AlternateViews.Add(htmlView);
System.Net.Mail.SmtpClient client = new SmtpClient();
client.Host = "smtp.office365.com";
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = "out#mydomain.com", "MyPassword");
client.Port = 587;
client.EnableSsl = true;
if (client.Host.Trim() != "")
client.Send(msg);
}
catch (Exception ex)
{
AuditLog.Info(string.Format("Failed to send mail . Error={0} ", ex.Message));
}
finally
{
AuditLog.Info("end");
}
}
And this is the error:
{"Transaction failed. The server response was: 5.2.0 STOREDRV.Submission.Exception:SendAsDeniedException.MapiExceptionSendAsDenied; Failed to process message due to a permanent exception with message Cannot submit message. 0.35250:0A00A280, 1.36674:0A000000, 1.61250:00000000, 1.45378:02000000, 1.44866:FD1E0000, 1.36674:0E000000, 1.61250:00000000, 1.45378:021F0000, 1.44866:14030000, 16.55847:AD0F0000, 17.43559:0000000004020000000000000000000000000000, 20.52176:140F2A8A0A00101043050000, 20.50032:140F2A8A7A17000000000000, 0.35180:48050000, 255.23226:0A00A780, 255.27962:0A000000, 255.27962:0E000000, 255.31418:0A00A880, 0.35250:0A000000, 1.36674:0A000000, 1.61250:00000000, 1.45378:02000000, 1.44866:20000000, 1.36674:32000000, 1.61250:00000000, 1.45378:25000000, 1.44866:01000000, 16.55847:8C000000, 17.43559:0000000030030000000000007B00000000000000, 20.52176:140F2A8A0A0070200A00AD80, 20.50032:140F2A8A7A1710106B050000, 0.35180:0A00AE80, 255.23226:4800D13D, 255.27962:0A000000, 255.27962:32000000, 255.17082:DC040000, 0.27745:75050000, 4.21921:DC040000, 255.27962..."}
It's right there in the error message: Exception:SendAsDeniedException.MapiExceptionSendAsDenied;
You will need to grant your users SendAs permissions for out#mydomain.com. That's probably not what you really want to do though, as those users would then consume an office 365 license and if they were using a license, they could just send as themselves.
The real solution is to not use office 365 as an SMTP relay. You should sign up with another SMTP provider that is specifically set up to do what you are trying to do. We use SendGrid, but there are others out there if you search.

SmtpClient Email Not Sent, Not Caught and Not Logged?

Our website was meant to send out a particular email, it didnt send the email and it also did not log the error which is unusual as i have a try catch setup which should log the entry in the try or catch section.
1) Why did it not log?
Should i not use Exception and instead use SmtpException and SmptFailedException. If i have this would it have made a difference? If so can you please provide an example.
2) The port we are currently using is 587. As our website is SSL i read we should be using 443 port instead. Would this have made a difference?
The below code is called asynchronously by using
ThreadPool.QueueUserWorkItem(new WaitCallback(SendInstructions), guid);
Which then calls a function that then calls the SendEmail Function below
public static bool SendEmail(String strToAddress, String strFromAddress, String strFromName, String strSubject, String strRecipientName, String strBody, String strEmbeddedImagePath, String strEmbeddedImageName, String strCCReminderEmail)
{
try
{
SmtpClient client = new SmtpClient();
client.EnableSsl = true;
using (MailMessage message = new MailMessage(
new MailAddress(strFromAddress, strFromName),
new MailAddress(strToAddress, strRecipientName)))
{
message.IsBodyHtml = true;
message.Subject = strSubject;
message.Body = strBody;
if (!string.IsNullOrEmpty(strCCReminderEmail))
message.CC.Add(strCCReminderEmail);
client.Send(message);
LogEmail(strFromAddress, strToAddress, strSubject, "Sent", strBody);
return true;
}
}
catch (Exception ex)
{
//log email
LogEmail(strFromAddress, strToAddress, strSubject, "Error", strBody);
throw;
}
}
I had a problem similar when using
ThreadPool.QueueUserWorkItem( new WaitCallback( ...
and then
System.Net.Mail.SmtpClient sC = new System.Net.Mail.SmtpClient(SMTPHost, SMTPPort);
the thread died without throwing an exception.
In my case, the main application thread is exiting before the background threads in the threadpool have had a chance to finish.
The threadpool creates Background worker threads which die as soon as
the main application thread terminates.
so I made a function to sleep 5 seconds and it worked.

System.Net.Mail.SmtpFailedRecipientException: Mailbox name not allowed

I have written ASP.Net code to send mails from domain1.com mail account such as abc#domain1.com. This code work fine otherwise and the mails go. But when the same code executes on domain2.com, even with correct userid-pwd it gives the following error:
System.Net.Mail.SmtpFailedRecipientException: Mailbox name not allowed. The server response was: sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1) at System.Net.Mail.SmtpClient.Send(MailMessage message)
Is there any way to fix this?
If we have to add this domain in the list of allowed rcphosts, how can that be done?
The code written is something like this:
MailMessage message;
bool success;
message = new MailMessage(from, to);
Attachment file;
SmtpClient lclient;
lclient = new SmtpClient("mail.domain1.com", 587);
lclient.EnableSsl = false;
message.Body = body;
message.BodyEncoding = System.Text.Encoding.UTF8;
message.IsBodyHtml = true;
message.Subject = subject;
message.SubjectEncoding = System.Text.Encoding.UTF8;
lclient.SendCompleted += new
SendCompletedEventHandler(SendCompletedCallback);
lclient.UseDefaultCredentials = false;
lclient.Credentials = new NetworkCredential(userID, password);
try
{
lclient.Send(message);
success = true;
if (message != null)
message.Dispose();
success = true;
return (success);
}
catch (Exception ex)
{
//...
}
Thanks
The code works fine. The error is a rejection from the SMTP server. It would seem that the server, when accessed from Domain1, allows you to forward mail through it. When accessed from Domain2, it does not. Changing this would be a configuration on the SMTP server.
Note that this is common practice for SMTP services. They generally don't allow anybody to send mail through them to any address. (That would leave them wide open for spammers and other such unwanted activities.) So, if you're trying to access Domain1's SMTP service from outside of Domain1, it's probably just rejecting that.

Not all e-mails being sent using SmtpClient in an ASP.NET MVC getting delivered. Why? How to resolve?

Here is the set-up:
I have a Notifications controller that is called from task scheduler 1x/day
The action method pulls upwards of 300 addresses, loops thru them and uses the SmtpClient class to send an individual e-mail to each recepient.
From what I can tell the process runs fine with no exceptions ... except that not all e-mails are being delivered. Anyone have any ideas on what is going on and how to resolve?
Here is the code:
foreach (var emp in division.Users)
{
var fromAddress = "myfromaddress";
var mailServer = "mymailserver";
var toEmail = emp.EmailAddress;
var message = new MailMessage(fromAddress, toEmail)
{
Subject = subject,
Body = "<body style='font:normal 13px tahoma,arial,helvetica;'>" + body + "</body>",
IsBodyHtml = true
};
var client = new SmtpClient(mailServer);
client.Send(message);
}
UPDATE:
Adding a pause in between sending e-mails resolves the problem. But why does this work? And is there a better way (e.g. using Async()) that would equally resolve the issue in a better way???
Updated code ...
foreach (var emp in division.Users)
{
var fromAddress = "myfromaddress";
var mailServer = "mymailserver";
var toEmail = emp.EmailAddress;
var message = new MailMessage(fromAddress, toEmail)
{
Subject = subject,
Body = "<body style='font:normal 13px tahoma,arial,helvetica;'>" + body + "</body>",
IsBodyHtml = true
};
var client = new SmtpClient(mailServer);
client.Send(message);
Thread.Sleep(3000); // Wait 3s until sending next message
}
there is a default timeout with the smtp client and default value is 100sec
more info here
If you are not having any exceptions I'd check SPAM folders and email addresses. I'd also try sending an email manually from your outlook to one of the addresses that didn't recieve a message.
On a side note, unless you are using different mail servers, I think you can change this code to
var client = new SmtpClient(mailServer);
var mailServer = "mymailserver";
foreach (var emp in division.Users)
{
var fromAddress = "myfromaddress";
var toEmail = emp.EmailAddress;
var message = new MailMessage(fromAddress, toEmail)
{
Subject = subject,
Body = "<body style='font:normal 13px tahoma,arial,helvetica;'>" + body + "</body>",
IsBodyHtml = true
};
client.Send(message);
}
You might also try SendAsync method of the SmtpClient class, like this:
// setup the callback method when the send finishes
client.SendCompleted += SendComplete; //new SendCompletedEventHandler(smtpSender_SendCompleted);
// send the email
client.SendAsync(message, null);
private void SendComplete(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
// do stuff on complete
}
I would strongly advice on using SmtpClient.SendAsync() as Send() is blocking.
I would check the logs of the SMTP server that you're sending to. Note: sending your own emails are more likely to end in Junk mail than using a trusted provider.
Edit: added sample SendAsync code
smtpClient.SendCompleted += smtpClient_SendCompleted;
static void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
Log.ErrorFormat("Async Mail exception {0} :: {1}",
e.Error.GetType().Name, e.Error.Message);
}
}

Actionscript 2.0 Email form LoadVars with ashx .net handler

I have a flash as 2.0 file that i need to send emails via an asp handler. First off, is this possible? Second, if it is, how do i get the return to have a status=true?
the .net codebehind
public void ProcessRequest(HttpContext context)
{
//E-Mail Method
string response = "sent=success";
MailAddress fromAddress = new MailAddress(context.Request.QueryString["Email"].ToString(), context.Request.QueryString["Name"].ToString());
MailAddress toAddress = new MailAddress("emailInbox#site.com", "Goons");
MailMessage message = new MailMessage(fromAddress, toAddress);
message.Subject = context.Request.QueryString["Name"].ToString() + " sent you a message from the website.";
message.Body = context.Request.QueryString["Msg"].ToString();
SmtpClient client = new SmtpClient("mail.grassrootsdm.com");
// Include credentials if the server requires them.
NetworkCredential SMTPUserInfo = new NetworkCredential("mailsenderemail","password");
client.Credentials = SMTPUserInfo;
try {
client.Send(message);
}
catch (Exception ex) {
response = ex.ToString();
}
context.Response.Write(response);
}
the actionscript
if (i == 0) {
sendVars.Name = fieldName.field.text;
sendVars.Email = fieldEmail.field.text;
sendVars.Msg = fieldMsg.field.text;
sendVars.sendAndLoad("http://www.grassrootsdm.com/WebService/EmailHandler.ashx", statusVars, "POST");
statusMsg.text = "Sending...";
statusVars.onLoad = function(success:Boolean) {
if (success) {
if (statusVars.sent == "success") {
clearForm();
statusMsg.text = "Message sent";
}
} else {
statusMsg.text = "Error!";
}
clearInterval(clearStatus);
clearStatus = setInterval(clearStatusInt, 3000);
};
}
Yes, it is possible.
Read the important notes at the bottom of each codes pertain to sending and retrieving data from flash to .net page. Explanation of the code is in the comment inside the code.
Flash Part (Action Script 2)
//function to send invoke .net page to send email
//use other control/button to call this function
//important: in order for the 'onLoad' event to work correctly, this function has to be 'Void'
function sendMail():Void
{
//show status
statusMsg.text = "Sending...";
//create LoadVars object
var lv_in:LoadVars = new LoadVars();
var lv_out:LoadVars = new LoadVars();
//set onLoad event
lv_in.onLoad = function(success:Boolean)
{
//if success, meaning data has received from .net page, run this code
if (success)
{
//lv_in.sent is used to parsed out message/data from .Net page
statusMsg.text = "Message sent!" + lv_in.sent;
}
//if fail, run this code
else
{
statusMsg.text = "Error!";
}
}
//begin invoke .net page to send email
lv_out.sendAndLoad("SendMail.aspx", lv_in, "POST");
}
Important note:
The function that contain onLoad event, in this case sendMail function, has to be Void function, meaning it's not returning value. If this function return value, what happen is the function will be executed all the way without waiting for the data for the return data from .net page, thus the onLoad event will not be set properly.
.Net Part
I copied the OP's .Net code so assuming this code works when sending email.
public void ProcessRequest(HttpContext context)
{
//E-Mail Method
string response = "sent=Success&";
MailAddress fromAddress = new MailAddress(context.Request.QueryString["Email"].ToString(), context.Request.QueryString["Name"].ToString());
MailAddress toAddress = new MailAddress("emailInbox#site.com", "Goons");
MailMessage message = new MailMessage(fromAddress, toAddress);
message.Subject = context.Request.QueryString["Name"].ToString() + " sent you a message from the website.";
message.Body = context.Request.QueryString["Msg"].ToString();
SmtpClient client = new SmtpClient("mail.grassrootsdm.com");
// Include credentials if the server requires them.
NetworkCredential SMTPUserInfo = new NetworkCredential("mailsenderemail","password");
client.Credentials = SMTPUserInfo;
try
{
client.Send(message);
}
catch (Exception ex)
{
response = ex.ToString();
}
context.Response.Write(response);
}
Important note:
The only thing I changed from the OP's .Net code is the response message. It was originally "sent=success" which I changed to "sent=success&".
The reason for this is, if you want action script to parse the posted message/data from .Net, it will stop at & symbol, thus leave the rest of the message alone and only get the message under sent variable.

Resources