0

我在让这段代码工作时遇到问题(对不起,长度)。代码经过消毒,仅显示诊断问题的相关部分。

从控制台应用程序运行时它工作正常。但是,当从 ASP.NET 应用程序调用 Utlities.SendBulkEmail 时,BulkEmailCompleted例程永远不会触发,并且正是这个例程增加了 m_CompletedWorkers 计数器。

如何重构 SendBulkEmail 例程以使用 AsyncOperationManager 而不是 BackgroundWorker,因此我可以保证返回结果的线程。

SendBulkEmail 例程本身不是多线程的。多线程发生在其 foreach 循环内。

我认为原始代码的基础是从这个网站获得的:http: //www.dotnetfunda.com/articles/article613-background-processes-in-asp-net-web-applications.aspx

Utilities 项目在各种解决方案之间共享,并且几乎是独立的。

我希望我能说清楚。

任何帮助将不胜感激。

代码如下:

在网站项目中 (Control.ascx.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Configuration;
using <company>.BusinessObjects.Emails;
using <company>.DataAccessors;
using <company>.DataObjects;
using <company>.Enumerations;
using <company>.Interfaces;
using <company>.Utilities;
    ...
        protected void sendButton_OnClick(object sender, EventArgs e)
        {
            ...
                if (HasBenefits)
                {
                    ReportingEmails emailer = new ReportingEmails();
                    ...
                    //Prevent send if nothing selected
                    if (... > 0)
                    {
                        List<EmailOutcome> results = emailer.GenerateNotificationEmailsForEmployer(<some int>, <some list>);
                        ...
                    }
                }
            ...
        }

在业务对象项目中

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Mail;
using System.Resources;
using System.Text;
using System.IO;
using <company>.BusinessObjects.Emails.Helpers;
using <company>.DataAccessors;
using <company>.DataObjects;
using <company>.Enumerations;
using <company>.Utilities;

namespace <company>.BusinessObjects.Emails
{  
    public class ReportingEmails
    {   
        ...
        public List<EmailOutcome> GenerateNotificationEmailsForEmployer(int employerID, List<int> benefits = null)
        {
            ...
            SendNotificationEmails(List<ReportData>, ref emailSuccessByBenefit, true, benefitsToExclude);
            return emailSuccessByBenefit;
        }

        private void SendNotificationEmails(List<ReporterCommsData> reportData, ref List<EmailOutcome> emailSuccessByBenefit, bool isFleet, List<int> benefitsToExclude)
        {
            Dictionary<int, MailMessage> bulkEmails = new Dictionary<int, MailMessage>();

            //build up the set of emails to send
            foreach (ReporterCommsData report in reportData)
            {                
                ...
                if (String.IsNullOrEmpty(report.Email))
                {
                    ...
                }
                else
                {
                    try
                    {
                        MailMessage email = null;
                        ...
                            email = ConstructEmail(<param>, out <param>, <param>);
                        ...
                        bulkEmails.Add(report.BenefitID, email); //add each email to the bulk email dictionary
                        ...
                    }
                    catch (Exception ex)
                    {   
                        ...
                    }
                }
            } //end foreach

            //do the bulk email send and get the outcomes
            try
            {
                ...
                emailSuccessByBenefit.AddRange(Utilities.Mail.SendBulkEmail(bulkEmails, credentials));
            }
            catch (Exception ex)
            {
                ...
            }
        }
        ...
    }
    ...
}

在公用事业项目中

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Threading;

namespace <company>.Utilities
{
    ...
    public class Mail
    {
        private static List<EmailOutcome> m_MultithreadedEmailSendResults = new List<EmailOutcome>();
        private static int m_CompletedWorkers = 0;
        ...
        /// <summary>
        /// Sends a large number of emails asynchronously and then reports success of the individual items collectively
        /// </summary>
        /// <param name="emails">A dictionary of completed MailMessage objects to send out, keyed on an ID</param>
        /// <param name="credentials">Network credentials which may be required to send the email</param>
        /// <returns>List of EmailOutcome objects signifying the success or failure of sending each individual email</returns>
        public static List<EmailOutcome> SendBulkEmail(Dictionary<int, MailMessage> emails, System.Net.NetworkCredential credentials = null)
        {
            const int NUMBER_OF_THREADS_PER_PROCESSOR = 1;
            m_CompletedWorkers = 0;

            List<EmailOutcome> results = new List<EmailOutcome>();
            List<Dictionary<int, MailMessage>> splitEmailList = new List<Dictionary<int, MailMessage>>();
            ...
            List<BackgroundWorker> workerThreads = new List<BackgroundWorker>();

            foreach (Dictionary<int, MailMessage> splitEmails in splitEmailList)
            {
                // Initialise the parameter array
                Object[] parameterArray = new Object[2];
                parameterArray[0] = splitEmails;
                parameterArray[1] = credentials;

                // Runs on function startup
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += new DoWorkEventHandler(BulkEmailWorker);
                worker.WorkerReportsProgress = false;
                worker.WorkerSupportsCancellation = true;
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BulkEmailCompleted);

                //Add worker to collection
                workerThreads.Add(worker);

                //Calling the BulkEmailWorker asynchronously
                worker.RunWorkerAsync(parameterArray);
            }

            //Hold until all background workers complete
            while (workerThreads.Count > m_CompletedWorkers)
            {
                Thread.Sleep(500); //Wait a half second
            }

            //Dispose of BackgroundWorkers
            foreach (BackgroundWorker worker in workerThreads)
            {
                worker.Dispose();
            }

            //Get results
            results.AddRange(m_MultithreadedEmailSendResults);

            //Clear the static variable
            m_MultithreadedEmailSendResults.Clear();
            m_MultithreadedEmailSendResults = new List<EmailOutcome>();

            return results;
        }
        ...

        /// <summary>
        /// Event handler for the RunWorkerCompleted event. Adds the EmailOutcome results to the static 
        /// </summary>
        private static void BulkEmailCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            //Add the EmailOutcome objects to the static list of results if completed
            if (worker != null)
            {
                Thread.Sleep(200);
                worker.RunWorkerAsync();
            }
            else
            {
                m_MultithreadedEmailSendResults.AddRange(e.Result as List<EmailOutcome>);
                m_CompletedWorkers++;
            }
        }
        ...
    }
    ...
}
4

1 回答 1

0

asp.net 的工作方式是它为每个请求创建一个 Page 对象。控件也是如此。您发送的电子邮件通知只能到达此对象。

您创建的线程将需要时间来执行,并且您的响应不会等待电子邮件发送完成。这意味着您不能使用相同的请求发送状态。

但是,如果您从页面发出另一个请求(ajax 或其他请求)以获取更新状态,则会创建一个新页面和相应的控件对象。您必须从静态对象中获取状态并使用它向用户显示状态。

您可能会发现UpdatePanel控件可以方便地实现 ajax。

于 2013-03-20T05:58:37.967 回答