4
votes

I don't want the user to wait for page completing the sending processes, so I'm thought of using SendAsync in ASP.NET 3.5. But for after debuging, I found that the Main Thread still waits.

Main: Call send email function...
mailSend: Configuring....
mailSend: setting up incorrect port....
mailSend: Attempt now to send....
mailSend: End of Line
Main: Email Function call finish.
Main: Proccess Complete!
mailComp: Sending of mail failed, will try again in 5 seconds...
mailComp: Retrying...
mailComp: Send successful!
mailComp: End of Line

Now the I placed incorrect port setting so the first email fails, and test to see if the second time it will be successful. Even with the correct port, the page still waits. Only after mailComp function is finish is when the Page is finally posted. Is this some limitation to SendAsyn?

And here some code, not sure if this will be helpful.


    protected void btnReset_Click(object sender, EventArgs e)
    {
        try
        {
            DataContext db = new DataContext();

            var query = from u in db.Fish
                        where u.Username == txtUsername.Text & u.Email == txtEmail.Text
                        select new { u.Username, u.Email };

            if (query.Count() != 0)
            {


                User user = new User();
                String token = user.requestPasswordReset(txtUsername.Text);
                String URL = Request.Url.AbsoluteUri.ToString() + "?token=" + token;
                String body = "Reseting you password if you \n" + URL + "\n \n ";


                Untilty.SendEmail(txtEmail.Text, "Reseting Password", body);

                litTitle.Text = "Message was sent!";
                litInfo.Text = "Please check your email in a few minuets for further instruction.";
                viewMode(false, false);
            }
            else
            {
                litCannotFindUserWithEmail.Visible = true;
            }


        }
        catch (Exception ex)
        {
            Debug.Write("Main: Exception: " + ex.ToString());
            litInfo.Text = "We are currently having some technically difficulty. Please try  again in a few minuets. If you are still having issue call us at 905344525";
        }
    }



///
public static class Utility

///

    public static void SendEmail(string recipient, string subject, string body)
    {
        MailMessage message = new MailMessage();

        message.To.Add(new MailAddress(recipient));
        message.From = new MailAddress(fromaddress, "Basadur Profile Website");
        message.Subject = subject;
        message.Body = body;

        Send(message);
    }

    private static void Send(MailMessage msg)
    {
        SmtpClient smtp = new SmtpClient();
        smtp.Host = "smtp.gmail.com";
        smtp.Credentials = new System.Net.NetworkCredential(fromaddress, mailpassword);
        smtp.EnableSsl = true;
        Debug.WriteLine("mailSend: setting up incorrect port....");
        smtp.Port = 5872; //incorrect port
        smtp.SendCompleted += new SendCompletedEventHandler(smtp_SendCompleted);

        smtp.SendAsync(msg, msg);

    }

    static void smtp_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        var msg = (MailMessage)e.UserState;

        if (e.Error != null)
        {
            System.Threading.Thread.Sleep(1000 * 5);

            try
            {
                SmtpClient smtp = new SmtpClient();
                smtp.Host = "smtp.gmail.com";
                smtp.Credentials = new System.Net.NetworkCredential(fromaddress, mailpassword);
                smtp.EnableSsl = true;
                smtp.Port = 587;
                smtp.Send(msg); 
            }
            catch (Exception ex)
            {
                Debug.WriteLine("mailComp: Failed for the second time giving up.");
            }
        }

    }

5
Where are you expecting the "Main" execution to resume? It can't restart any earlier than the SendAsync call.Barry
Well, I was thinking that when I use SendAsyc, the Main execution will resume and set a label to say "Message send" and then posted to the user. Then user will regain control, even if the sending process is still working in the background on the server. My debuging tells me this happens all asynchronous, but the Main still wait before posting.Jerad
Different topic, you should definitely not doing that kind of work insidebtnReset_Click. You should be passing out of your presentation layer to a business service layer. Also you should never be using EF contexts directly anywhere except a data layer that is business agnostic. Using an EF context in a presentation layer is one of the worst anti-patterns in existence.Chris Marisic
Try putting your synchronous method under Task.Run block like Task.Run(()=>Your method here); or make your method asynchonous using async-await call. In latest .net framework these features are available.Rahul Jha

5 Answers

3
votes

In .NET 4.5.2, a method was added for scheduling tasks in the background, independent of any request: HostingEnvironment.QueueBackgroundWorkItem().

HostingEnvironment.QueueBackgroundWorkItem() is in the System.Web.Hosting namespace.

The remarks in the documentation say:

Differs from a normal ThreadPool work item in that ASP.NET can keep track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.

Meaning, you can fire off a task in a fire-and-forget manner with a greater confidence in it not being terminated when the app domain recycles.

Usage is like:

HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
{
    try 
    {
       // do work....
    }
    catch(Exception)
    {
        //make sure nothing can throw in here
    }
});
1
votes

These days you can use a Task to dispatch work to the thread pool. Just use

Task.Run(()=>  Untilty.SendEmail(txtEmail.Text, "Reseting Password", body));
0
votes

The debugger seems to force things to be single threaded. If you step through, you may find that you end up jumping between two different files, as each step moves a different thread.

Try to add some logging in there and run without debugging.

0
votes

Using the below code you can send async emails in asp.net with SmtpClient.

ThreadPool.QueueUserWorkItem(callback =>
                {
                    var mailMessage = new MailMessage
                    {
                        ...
                    };

                    //if you need any references in handlers
                    object userState = new ReturnObject
                    {
                        MailMessage = mailMessage,
                        SmtpClient = smtpServer
                    };
                    smtpServer.SendCompleted += HandleSmtpResponse;
                    smtpServer.SendAsync(mailMessage, userState);
                });
-3
votes

Use threading:

// Create the thread object, passing in the method
  // via a ThreadStart delegate. This does not start the thread.
  Thread oThread = new Thread(new ThreadStart(SendMyEmail));

  // Start the thread
  oThread.Start();

Here is the source, it has a full threading tutorial.