fligant
fligant

Reputation: 732

send mail in new Thread in mvc taking time on hosting

I try to send mail and i don't want to wait for the result , i create new thread and send the mail its working on my pc but when i build the application and upload it to shared hosting its taking to match time like no new thread created and mail send successfully .

 Thread sendMailThread = new Thread(() =>
            sendMail(to, body, subject));
            sendMailThread.Start();


        public bool sendMail(string to, string body,string subject)
    {
        try
        {
            SmtpClient client = new SmtpClient(smtpServer);
            //If you need to authenticate

            client.Port = smtpPort;
            client.EnableSsl = enableSsl;
            client.DeliveryMethod = SmtpDeliveryMethod.Network;
            client.UseDefaultCredentials = false;
            client.Credentials = new NetworkCredential(smtpUser, smtpPass);

            MailMessage mailMessage = new MailMessage();
            mailMessage.From = new MailAddress(defaultSender);
            mailMessage.To.Add(to);
            mailMessage.Subject = subject;
            mailMessage.Body = body;

            client.Send(mailMessage);
            return true;
        }
        catch(Exception ex)
        {
            return false;
        }
    }

update controller action

 [System.Web.Http.HttpGet]
    public void changePassword(string userId, string newPassword)
    {
        var context = new ApplicationDbContext();
        UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
        ApplicationUser cUser = userManager.FindById(userId);

        if (String.IsNullOrEmpty(newPassword)) newPassword = "123456";

        String hashedNewPassword = userManager.PasswordHasher.HashPassword(newPassword);

        UserStore<ApplicationUser> store = new UserStore<ApplicationUser>();
        store.SetPasswordHashAsync(cUser, hashedNewPassword);
        store.UpdateAsync(cUser);
        context.SaveChanges();

        string mailBody = "Your password has been changed with user name : " + cUser.UserName + " and password: " + newPassword;
    //send mail
        mailService.sendMailThread(cUser.Email, mailBody, "Your password has been changed");
    }

update why i need to send mail in new thread ? i wan't want response wait until mail send , as it take some time .

update this code is working perfect on my local machine with the same SMTP server , but when i upload the site to shared hosting the request taking match time until its returned to browser , and i think send mail blocking my main request thread .

Upvotes: 0

Views: 971

Answers (3)

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131180

First, you don't need a separate thread to send an email asynchronously. You can use SenMailAsync, eg:

public async Task<bool> sendMailAsync(string to, string body,string subject)
{
    ...
    await client.SendMailAsync(mailMessage);
    ...
}

public async Task MyAction(...)
{
    ...
    var success=await sendMailAsync(to,body,subject);
    if(!success)
    {
        Log.Error("Sending failed but I don't know why because I discarded the exception");
    }
    ...
}

That won't make sending faster though, it means that you don't have to block the request thread while waiting for sending to complete. Using a separate thread won't complete faster either. After all, the remote server won't work faster if you use the request thread or another thread.

In fact, if you use a background thread and wait for it to complete, you harm performance. The request thread will be blocked waiting for another thread to do what the request thread could do as easily. With await the request thread is released and execution resumes after await.

Web sites and applications typically use a local smtp server or even IIS's SMTP service to send emails immediatelly and avoid waiting for responses from remote servers. You could build a queue to send emails asynchronously, but that's exactly what the SMTP service does in the first place.

UPDATE

If you can't use an SMTP Service and don't won't to wait for the send operation, you'll have to create a fire-and-forget action in a safe way. Simply using a Thread, or removing await isn't enough. ASP.NET can recycle an AppDomain for any number of reasons, terminating your thread/task with it. Scott Hanselman explains the various techniquest used to run a background task safely. One of them is to tell ASP.NET that you have a background task, with QueueBackgroundWorkItem. IIS will wait up to 90 seconds for a background task to terminate before recycling.

To register a background email task :

public async Task MyAction(...)
{
    ...
    HostingEnvironment.QueueBackgroundWorkItem(ct =>sendMailAsync(to,body,subject));
    ...
}

Upvotes: 2

Arikael
Arikael

Reputation: 2085

We had similar problems. They were usually caused by one of the following reasons

  • email server is slow
  • network issues
  • relaying issues (your local connection probably accesses your SMTP Server differently than via your hosting)
  • using the wrong port (we had the problem that multiple ports were possible, but some, probably deprecated ones, were significantly slower
  • using unsecure connection where a SSL Connection was expected (again, it worked but much slower)

that means usually the client code itself wasn't really the problem.

You could check the port on which you connect to your SMTP Server and try sending it with and without an SSL connection.

Or you can, as mentioned by others, send your mails asynchronously. Either use some BackgroundWorker, Task, just fire and forget (probably not an ideal solution) or by using a fullfledged framework like hangfire

I think the error/problem is not in your code but somewhere in the communication between shared hosting and your SMTP Server.

Upvotes: 0

Anup Shetty
Anup Shetty

Reputation: 571

public void SendAsyncMail()
{
    MailMessage mail = new MailMessage();

    mail.From = new MailAddress("Enter from mail address");
    mail.To.Add(new MailAddress("Enter to address #1"));
    mail.To.Add(new MailAddress("Enter to address #2"));
    mail.Subject = "Enter mail subject";
    mail.Body = "Enter mail body";

    SmtpClient smtpClient = new SmtpClient();
    Object state = mail;

    //event handler for asynchronous call
    smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
    try
    {
        smtpClient.SendAsync(mail, state);
    }
    catch (Exception ex) { /* exception handling code here */ }
}

void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
    MailMessage mail = e.UserState as MailMessage;

    if (!e.Cancelled && e.Error!=null)
    {
        message.Text = "Mail sent successfully";
    }
}

Upvotes: 0

Related Questions