MVC 5.1, .NET 4.5
In our simple web site we need to send an email. Our email service is called by a Post back event and it sends the email, then builds a result page and returns it.
This slows the page since the page has to wait for the synchronous email to be sent.
If we run the email in a different thread, can the site continue on to create the page and return it to the user as the email is being processed, or will a long running task cause the page to be not rendered.
Basically, can I send am email non blocking for performance reasons.
Note: We can't send the email request to a service or other system.
Assuming that you you use built in SmtpClient you can use SmtpClient.SendMailAsync method
smtpClient.SendMailAsync(new MailMessage{ ... });
Sends the specified message to an SMTP server for delivery as an asynchronous operation
Calling StartNew is functionally equivalent to creating a task by using one of its constructors, and then calling the Task.Start method to schedule the task for execution.
Task.Factory.StartNew(() => {
//do somethings
});
Example:
Task.Factory.StartNew(() => {
SendMail("Hello world", "email#yahoo.com", "TEST");
});
public static bool SendMail(string subject, string to, string body)
{
string fromMailAddress = ConfigurationManager.AppSettings["MailAddress"];
string fromMailPassword = ConfigurationManager.AppSettings["MailPassword"];
string fromMailName = ConfigurationManager.AppSettings["MailName"];
var networkConfig = new NetworkCredential(fromMailAddress, fromMailPassword);
var mailServer = new SmtpClient()
{
Host = ConfigurationManager.AppSettings["SmtpHost"],
UseDefaultCredentials = false,
Credentials = networkConfig
};
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["SmtpPort"]))
mailServer.Port = Convert.ToInt32(ConfigurationManager.AppSettings["SmtpPort"]);
var message = new MailMessage()
{
Subject = subject,
SubjectEncoding = Encoding.UTF8,
IsBodyHtml = true,
BodyEncoding = Encoding.UTF8,
};
//message send config
message.To.Add(new MailAddress(to));
message.From = new MailAddress(fromMailAddress, fromMailName);
message.Body = body;
try
{
mailServer.SendAsync(message, null);
}
catch (Exception ex)
{
return false;
}
return true;
}
Related
I am having an issue with SmtpClient in an ASP.NET web application.
I have a generic function that builds an email message and then sends it. The code is as follows:
public static bool SendMessage( string fromName, string toName, string subject, string body ) {
var smtpClient = new SmtpClient("server address here")
{
Port = 587,
Credentials = new NetworkCredential("user", "pass"),
EnableSsl = false,
};
var mailMessage = new MailMessage
{
From = new MailAddress("sender", "Testing"),
Subject = subject,
Body = body
};
mailMessage.To.Add ( new MailAddress(toName, "Valued Customer") );
try {
smtpClient.Send ( mailMessage );
return true;
}
catch (Exception ex) {
var error = $"ERROR :{ex.Message}";
return false;
}
}
The problem is, I get the following error when I call it:
Mailbox unavailable. The server response was: <email address being sent to> No such user here
Naturally I removed the value in the < >, but in the original error message it is the email address of the recipient. I almost think the SMTP server believes the recipient has to be a user on the system.
What can I try next? I even hard-coded email addresses in rather than using variables, thinking maybe there was some weird issue with that, but it didn't work.
The error is telling you that the the SMTP server does not have a user with that email address (usually it has to do with security around the FROM address). The SMTP server will not send email if it does not recognize the FROM address.
Solution, change your FROM. Example:
var mailMessage = new MailMessage
{
From = new MailAddress("tester", "test#adminsystem.com"),
Subject = subject,
Body = body
};
I have a Contact page where users are able to complete a form which is sent to a Controller.
This Controller generates an email based on form data and returns a success view to the user.
The problem is that the success view is not displayed until email sending is completed, but this takes too much time (around 30 seconds), which is not acceptable.
First, I tried synchronous sending:
public ActionResult Contact(string TestMessage)
{
// Prepare SMTP client
SmtpClient Client = new SmtpClient()
{
DeliveryMethod = SmtpDeliveryMethod.Network,
EnableSsl = true,
Host = "smtp.test.com",
Port = 587,
UseDefaultCredentials = false,
Credentials = new System.Net.NetworkCredential("smtpuser", "smtppass")
};
// Prepare message
MailMessage MailMessage = new MailMessage()
{
From = new MailAddress("sender#mydomain.com"),
Subject = "Test",
BodyEncoding = System.Text.Encoding.UTF8,
SubjectEncoding = System.Text.Encoding.UTF8
};
MailMessage.To.Add(new MailAddress("recipient#mydomain.com"));
MailMessage.Body = TestMessage;
// Send mail
Client.Send(MailMessage);
// Return success view
return View("ContactSuccess");
}
After that I tried asynchonous sending. I have spent hours reading StackOverflow questions to find the miracle solution. But the result is always the same, page is still waiting for email sending to be completed.
Asynchronous attempt 1
// Send mail
Client.SendCompleted += (s, e) =>
{
Client.Dispose();
MailMessage.Dispose();
};
System.Threading.ThreadPool.QueueUserWorkItem(o =>
Client.SendAsync(MailMessage, Tuple.Create(Client, MailMessage)));
Asynchronous attempt 2
System.Threading.Tasks.Task.Run(() => SendViaAsync(MailMessage));
Asynchronous attempt 3
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
cancellationToken => Client.Send(MailMessage)
);
Asynchronous attempt 4
// Send mail
System.Threading.ThreadPool.QueueUserWorkItem(SendViaAsync, MailMessage);
With added methods:
private void SendViaAsync(object MailMessageObject)
{
MailMessage MailMessage = (MailMessage)MailMessageObject;
SmtpClient Client = Utilities.CreateEmailClient();
Client.SendCompleted += new SendCompletedEventHandler(SmtpClient_SendCompleted);
Client.Send(MailMessage);
}
void SmtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
MailMessage mail = e.UserState as MailMessage;
if (!e.Cancelled && e.Error != null)
{
// error handling
}
}
Asynchronous attempt 5
public async System.Threading.Tasks.Task<ActionResult> Contact(string TestMessage)
// ...
// Send mail
await SendEmail(MailMessage);
With added method:
public async System.Threading.Tasks.Task SendEmail(MailMessage MailMessage)
{
SmtpClient Client = new SmtpClient()
{
DeliveryMethod = SmtpDeliveryMethod.Network,
EnableSsl = true,
Host = "smtp.test.com",
Port = 587,
UseDefaultCredentials = false,
Credentials = new System.Net.NetworkCredential("smtpuser", "smtppass")
};
await Client.SendMailAsync(MailMessage);
}
Please note that when locally testing, email is sent immediately (in both synchronous or asynchronous modes). In production only, it takes around 30 seconds. So maybe there is something like an antivirus scan in my hosting provider, but anyway I can't figure out why the success view is not sent until mail sending is completed.
I am desperate. Any help would be much appreciated.
Any attempts with await will definitely fail to accomplish what you want by definition. But in general I don't think it's wise to have an asp.net page starting up processes which may keep running beyond the lifetime of the request. And perhaps that's exactly what's happening internally: ASP.Net not finalising the request until all its child processes have finished.
Personally, I would de-couple the process altogether. First because each part will be simpler to maintain, but also because it's more secure.
Have your application dump the email, or the data to build the email, into a queue of sorts. This could be a database, a file, a folder or whatever, but some temporary storage.
Then develop a background service or daemon, which reads this queue and processes the emails. It will end up being more flexible, more scalable and safer.
I think I have understood the problem. I can't believe it.
I didn't mention it in my bug reproduction, but I am using Google reCAPTCHA in the contact form.
It seems that reCAPTCHA is very slow in production environment!
After deactivating it, it appears that email is immediately sent.
Now, I just have to replace reCAPTCHA with a faster checking mecanism.
Sorry for disturbance, I hope this will help some of us later. At least some asynchronous programming examples are listed here.
There is a builtin functionality in asp net you can use it as easy as written below
public ActionResult SendEmail(User user)
{
HostingEnvironment.QueueBackgroundWorkItem(ct =>
{
// Prepare SMTP client
SmtpClient Client = new SmtpClient()
{
DeliveryMethod = SmtpDeliveryMethod.Network,
EnableSsl = true,
Host = "smtp.test.com",
Port = 587,
UseDefaultCredentials = false,
Credentials = new System.Net.NetworkCredential("smtpuser", "smtppass")
};
// Prepare message
MailMessage MailMessage = new MailMessage()
{
From = new MailAddress("sender#mydomain.com"),
Subject = "Test",
BodyEncoding = System.Text.Encoding.UTF8,
SubjectEncoding = System.Text.Encoding.UTF8
};
MailMessage.To.Add(new MailAddress("recipient#mydomain.com"));
MailMessage.Body = TestMessage;
// Send mail
Client.Send(MailMessage);
});
// Return success view
return View("ContactSuccess");
}
if you want to go over advance scenarios , like handling many background jobs you can use Hangfire library
this blog post will also help you solve your problem
this is how when I need to send email gives me error. But the mistake that since gives me is this:
An asynchronous operation cannot be started at this time. Asynchronous
operations may only be started within an asynchronous handler or
module or during certain events in the Page lifecycle. If this
exception occurred while executing a Page, ensure that the Page is
marked <%# Page Async="true" %>. This exception may also indicate an
attempt to call an "async void" method, which is generally unsupported
within ASP.NET request processing. Instead, the asynchronous method
should return a Task, and the caller should await it.
I have accumulated since the MVC and have used class to keep track of ie areas of the page. The reason I have used SendAsync is precisely that it goes a little faster to send email, etc..
This error only happens when I try to send email to users.
public static void NewPassword(string mail, string name, string password)
{
MailDefinition oMailDefinition = new MailDefinition();
oMailDefinition.BodyFileName = "~/MailList/emailskabelon/NewPassword.html";
oMailDefinition.From = FromMail;
Dictionary<string, string> oReplacements = new Dictionary<string, string>();
oReplacements.Add("<<navn>>", name);
oReplacements.Add("<<password>>", password);
System.Net.Mail.MailMessage oMailMessage = oMailDefinition.CreateMailMessage(mail, oReplacements, new LiteralControl());
oMailMessage.Subject = NewpasswordTitle + WebsiteName;
oMailMessage.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient(AzureApi);
System.Net.NetworkCredential netcred = new System.Net.NetworkCredential(AzureName, AzurePassword);
smtp.UseDefaultCredentials = false;
smtp.EnableSsl = true;
smtp.Credentials = netcred;
smtp.Port = Convert.ToInt32("25");
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
using (var smtpClient = new SmtpClient())
{
smtp.SendAsync(oMailMessage, null);
}
}
I've tried to do like this:
public static async NewPassword(string mail, string name, string password)
{
....
using (var smtpClient = new SmtpClient())
{
await smtp.SendAsync(oMailMessage, null);
}
i have see here: https://stackoverflow.com/a/35212320/7391454
Change your method to:
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);
}
}
And call it like:
var task = SendEmail(toEmailAddress, emailSubject, emailMessage);
var result = task.WaitAndUnwrapException();
Have a look here Asynchronously sending Emails in C#?
and here How to call asynchronous method from synchronous method in C#?
You can also try to define your async option inside a separate thread.
I believe that you already have inserted the async tag in your page.
And if everything is okay then try to put your code in below block.
this.Page.RegisterAsyncTask(new PageAsyncTask(async ctoken => {
var result = await SomeOperationAsync(ctoken);
// result operations.
}));
Not really replying your original question, but just wanted to stress that you are better off calling an email sending code without keeping the calling thread waiting. Although you are using async/await, in user's perspective, you are still waiting in the browser while the server is done sending emails. It may be few milliseconds, but still it's better to let this handled by a background worker.
So IMO, using HostingEnvironment.QueueBackgroundWorkItem(x=> SendEmail()); would be a better approach.
Having said that, you still have a slight risk of the asynchronous task being terminated if app domain recycles in the middle. But that's highly unlikely in your case I would say. Even if that happens, you can use a cancellation token and work your way around it.
I need to send about 5000 emails as newsletter to my website users asynchronously.. The problem is i don't know the best way to send them asynchronously ..Please help me updating my code to make it asynchronously
My Code:
public string SendEmail()
{
foreach (var emailAddress in EmailList)
{
var message = new MailMessage("myemail#gmail.com", emailAddress);
message.Subject = "hi";
SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
client.Credentials = new NetworkCredential("myemail#gmail.com", "*****");
client.EnableSsl = true;
client.Send(message);
}
return "done";
}
Thank you , Lucy
Take a look at the async and await keywords.
https://msdn.microsoft.com/en-us/library/mt674882.aspx
The async and await keywords in C# are the heart of async programming. By using those two keywords, you can use resources in the .NET Framework or the Windows Runtime to create an asynchronous method almost as easily as you create a synchronous method. Asynchronous methods that you define by using async and await are referred to as async methods.
MSDN explains the syntax side of things. The bigger concern is error handling and reliably. Dumping 5,000 emails into a list and hitting the "send" button on them is a little optimistic. Do these emails need to be reliably delivered? What happens if 3,000 of them send, and a network error suddenly causes temporary connectivity loss to the outgoing mail server? Are you going to resend all 5,000 when it starts working again? Just forget about the last 2,000? Are the recipients going to be mad because they got duplicates, or didn't get the message at all? How are you going to troubleshoot errors?
A pattern that I've found that has worked really well (whether you are sending synchronously or asynchronously), is to generate the messages and store each in a database table, and then use something like the following:
public void SendAllEmails()
{
var emails = SomeClass.GetAllUnsentEmails();
foreach(Email message in Emails)
{
var success = SendEmail(message);
if (!success)
{
// Do you want to do something if it fails?
}
}
}
public bool SendEmail(Email message)
{
try
{
// 1. Send the email message
// 2. Update the "SentOn" date in the database
// 3. return true
}
catch(Exception ex)
{
SomeClass.CreateEmailErrorEntry(message, ex); // store error in a table or log
return false;
}
}
edit: if it's being used inside of action you should mark your action async
public async Task<ActionResult> MyAction()
for a start, don't create SmtpClient for every message so as follows
and also this, should send the mails asynchronously but wait for all of them to be sent
public string SendEmail()
{
var tasks = new List<Task>();
var client = new SmtpClient("smtp.gmail.com", 587);
client.Credentials = new NetworkCredential("myemail#gmail.com", "*****");
client.EnableSsl = true;
foreach (var emailAddress in EmailList)
{
var message = new MailMessage("myemail#gmail.com", emailAddress);
message.Subject = "hi";
tasks.Add(client.SendMailAsync(message));
}
while(tasks.Count > 0)
{
var idx = Task.WaitAny(tasks.ToArray());
tasks.RemoveAt(idx);
}
return "done";
}
I have a small console application. It checks a few settings, makes some decisions, and sends an email. The problem is the email doesn't actually get sent until my application finishes. I want the email sent as soon as my method that sends the email completes.
Initially, I just created a MailMessage and called .Send(). That's when I noticed the mail was not being sent until the app finished.
Then I tried using the task parallel library.
var taskA = Task.Factory.StartNew(() => msg.Send());
Again, the messages don't get sent until my entire application finishes.
How do I sent an email when msg.send executes, not when the app completes?
SmptClient supports async sending of mail via SendAsync, however in practice in a web application this hangs the request thread.
To avoid blocking I recommend using the ThreadPool to fire off the email in a background thread. This won't block your application.
ThreadPool.QueueUserWorkItem(o => {
using (SmtpClient client = new SmtpClient(...))
{
using (MailMessage mailMessage = new MailMessage(...))
{
client.Send(mailMessage, Tuple.Create(client, mailMessage));
}
}
});
The most sure fire way to avoid delays would probably be to use a pickup directory, which will queue the message rather than send it immediately.
you should use a SMTP client. do it like this:
MailMessage mm = new MailMessage();
//fill in your message
NetworkCredential nc = new NetworkCredential(FromAddress, FromPassword);
SmtpClient sc = new SmtpClient(SmtpHost, SmtpPort);
sc.EnableSsl = true;
sc.Credentials = nc;
sc.Send(mm);
at this stage your mail will be sent.
But, sending an email is an async act, so it will take some time until you recive the mail.
Create a new MailMessage and send it with SmtpClient. It will send immediately. I will add an example.
EDIT: Populate the variables host, port with the smtp ser ver name and port number.
using (var mailer = new SmtpClient(host, port))
{
using (var message = new MailMessage(sender, recipient, subject, body) { IsBodyHtml = false })
{
mailer.UseDefaultCredentials = false;
mailer.Credentials = new NetworkCredential(user, pass);
mailer.EnableSsl = useSSL;
mailer.Timeout = Timeout;
mailer.Send(message);
}
}
If you still experience a delay, then the delay will be at the mail server.
Simply dispose the MailMessage and SmtpClient objects after the .Send() function.
SmtpClient smtpClient = new SmtpClient("server", 25);
smtpClient.UseDefaultCredentials = true;
MailMessage message = new MailMessage("ToAddress","FromAddress");
message.Subject = "Test email";
message.Body = "Test email";
smtpClient.Send(message);
message.Dispose();
smtpClient.Dispose();
Use SmtpClient with setting:
smtpClient.ServicePoint.MaxIdleTime = 2;
https://weblogs.asp.net/stanleygu/tip-14-solve-smtpclient-issues-of-delayed-email-and-high-cpu-usage