我遇到一个问题,让这块代码工作(抱歉长度)。该代码经过消毒,只是显示诊断问题的相关部分。
从控制台应用程序运行时,它工作正常。 但是当从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项目在各种解决方案中共享,并且几乎是独立的。
我希望我明白这一点。
任何帮助将不胜感激。
代码如下:
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++;
}
}
...
}
...
}
答案 0 :(得分:0)
asp.net的工作方式是为每个请求创建一个Page对象。控制也是如此。您发送的电子邮件通知只能到达此对象。
您创建的线程将花费时间执行,您的响应将不会等待电子邮件发送完成。这意味着您无法使用相同的请求发送状态。
但是,如果您从页面发出另一个请求,ajax或其他内容,以获取更新状态,将创建一个新的页面和相应的控制对象。您必须从静态对象获取状态,并使用它来向用户显示状态。
您可能会发现UpdatePanel控件可以实现ajax。