开发者

Set a background process and allow the user to continue without waiting

开发者 https://www.devze.com 2022-12-08 09:59 出处:网络
I have a newsletter tool that I am trying to setup to run as a background process to send out the emails. The code below works without any issues but the problem I have is that it is slow.

I have a newsletter tool that I am trying to setup to run as a background process to send out the emails. The code below works without any issues but the problem I have is that it is slow.

If there are 50 emails to send it can be very slow for the end user as they have to stare at the screen for up to 1min 30secs. This becomes a bigger problem for me if they client is sending an email to a larger group of people.

The reason I send each mail individually as apposed to sending 1 and bcc'ing the email list is that each email contains certain specific content for each user - like unsubscribe link codes, personal name at the start of the mail, etc.

I am looking for a solution where I can let the user click on a button and have .net run the sending email part in the background while the front end user is brought to a page saying that their email is being sent. Ideally, it should take no longer than a regular postback for all that to occur -开发者_StackOverflow社区 not the current few minutes.

Any thoughts on how best to achieve this?

Thanks for your help, Rich

if (Page.IsPostBack)
    {
        if (JustMeButton.Checked)
        {
            SendMail("emailme@address", EmailTemplate);
        }
        if (EveryoneButton.Checked)
        {
            //setup background process
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = false;
            bw.WorkerSupportsCancellation = false;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();

            //bring user to next screen and display message
            Response.Redirect("emailSendingMessageScreen.aspx");
        }
    }

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    DataTable emailTable = (DataTable)Session["emailTable"];
    foreach (DataRow row in emailTable.Rows)
    {
        SendMail(row["email"], row["name"], EmailTemplate);
    }
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!(e.Error == null))
    {
        SendMail("admin@address", "Error sending <br><br>" + e.Error.Message);
    }
    else
    {
        SendMail("admin@address", "emails sent successfully.");
    }
    //clear out the sessions created for sending this email
    Session.Remove("emailTable");
}

private void SendMail(string email, string emailMessage)
{
    MailMessage mailMessage = new MailMessage();
    mailMessage.From = new MailAddress("from@address");
    mailMessage.To.Add(new MailAddress(email));
    mailMessage.Subject = Server.HtmlEncode(EmailSubject.Text.Trim());
    mailMessage.Body = emailMessage;
    mailMessage.IsBodyHtml = true;

    SmtpClient smtpClient = new SmtpClient();

    Object userState = mailMessage;

    smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
    smtpClient.Timeout = 10000;

    try
    {
        smtpClient.SendAsync(mailMessage, userState);
    }
    catch (SmtpException smtpExc)
    {
        MailMessageTxt.Text += "Error Code: " + smtpExc.StatusCode;
        MailMessageTxt.Visible = true;
    }
    catch (Exception ex)
    {
        MailMessageTxt.Text += "Error is: " + ex;
        MailMessageTxt.Visible = true;
    }
}

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

    if (e.Error != null)
    {
        MailMessageTxt.Text = "Error occured, info=" + e.Error.Message;
        MailMessageTxt.Visible = true;
    }
}


I did this very thing sending a newsletter with the BeerHouse in the new version. You can get the book now and the source code is on CodePlex, http://thebeerhouse.codeplex.com/ http://professionalaspnet.com/archive/2009/10/07/ASP.NET-3.5-Problem-1320-Design-2D00-Solution.aspx alt text http://Professionalaspnet.com/images/187586-fg0705.jpg

The Solution uses AJAX to send the e-mails and allows the user to keep browsing around the site without being concerned about the newsletter being sent out. When it is done it just takes care of itself and the user can check on as they want. Its chapter 7 in the book, enjoy.


A thread created within an ASP page will get killed if the ASP worker process is recycled for whatever reason. A Windows service that performs the task via a message queue is ideal for long running jobs. Another "trick" solution is using cache expiry, explained here: http://www.codeproject.com/KB/aspnet/ASPNETService.aspx


Move all the work of sending the email to separate class and run it using ThreadPool

MailSender sender = new MailSender(parameters. ....);
ThreadPool.EnqueueUserItem(sender.sendAllEmails)

Using background worker won't work. It will be disposed when it goes out of context, meaning on Response.End


I have found trying to do tasks like this within the ASP.NET process is problematic as you cannot guarantee the process will complete or be successful. If the process gets cut off you have no recovery. I would have all your emails saved to a DB first and then have a service that polls for new entries in this database or table that handles the actual sending of the emails.

The advantage to this is that if your email provider or ASP.NET process goes down you don't lose any emails and you have a history of all emails sent with their details of when, who, etc... You can also rip out or change the emailer to do more, like send to Twitter or a phone text message etc. This effectively decouples your notifications from your application.

All applications I have made recently use this type of model and it has stopped emails from being lost due to service failures and other reasons. It has also made it possible to lookup all emails that have gone through the system and get metrics that allows me to optimize the need to send emails by storing extra information in the email record like reason sent, if it's to report an error, etc... Adding on additions such as routing notifications (eg go to text message instead if email) based on time of day or user has been possible with no changes to the primary applicaton.


Simply use ajax to execute the process.let the user continue their activity while the server bares the burden.

0

精彩评论

暂无评论...
验证码 换一张
取 消