4

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.");
            }
        }

    }

4
  • Where are you expecting the "Main" execution to resume? It can't restart any earlier than the SendAsync call. Commented Jun 29, 2011 at 15:01
  • 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. Commented Jun 29, 2011 at 15:11
  • 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. Commented Feb 16, 2016 at 19:56
  • 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. Commented Aug 2, 2020 at 14:55

5 Answers 5

3

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
    }
});
Sign up to request clarification or add additional context in comments.

1 Comment

This should work, I'm going to try using it myself, but note that there are some issues around Domain Teardown with it, where the work may or may not complete on say, server restart.
1

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));

Comments

0

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.

2 Comments

The debugger definitely handles threading fine, it can get very hairy debugging methods that are being called concurrently (debugging multiple stack frames at the same time when you keep hitting F10). I believe VS2015 attempted to improve this.
@ChrisMarisic It very much depends on the VS version, and my five year old answer is directed at VS2008, which is what the question was about.
0

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);
                });

Comments

-3

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.

4 Comments

Yes it does, but when you start it in a new thread you gain multi-tasking in your application, i.e. the ongoing thread continues normally and another thread goes to handle e-mail sending for you.
This is what SendAsync already does. There's no reason to complicate it by manually managing threads.
But as the OP stated, the page waits, which means SendAsync is not working.
dont do this in ASP.NET... IIS will barf if you have an unhandled exception inside a thread

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.