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

    }

Jerad
  • 709
  • 5
  • 11
  • Where are you expecting the "Main" execution to resume? It can't restart any earlier than the SendAsync call. – Barry Jun 29 '11 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. – Jerad Jun 29 '11 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. – Chris Marisic Feb 16 '16 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. – Rahul Jha Aug 02 '20 at 14:55

5 Answers5

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
    }
});
Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
Brad Rem
  • 6,036
  • 2
  • 25
  • 50
  • 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. – Serexx Nov 09 '20 at 09:35
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));
Lineker
  • 305
  • 1
  • 9
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);
                });
ilker
  • 166
  • 2
  • 3
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.

Andy
  • 8,432
  • 6
  • 38
  • 76
  • 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. – Chris Marisic Feb 16 '16 at 19:54
  • @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. – Andy Feb 16 '16 at 23:13
-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.

Ken D
  • 5,880
  • 2
  • 36
  • 58
  • 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. – Ken D Jun 29 '11 at 15:58
  • This is what SendAsync already does. There's no reason to complicate it by manually managing threads. – Andy Jun 29 '11 at 16:33
  • But as the OP stated, the page waits, which means `SendAsync` is not working. – Ken D Jun 29 '11 at 19:19
  • dont do this in ASP.NET... IIS will barf if you have an unhandled exception inside a thread – Ralph N Jan 05 '12 at 18:39