Использование конечной точки веб-службы для отправки электронной почты - хорошая идея, независимо от того, вызываете ли вы ее из класса aspx или из клиента с javascript.
Просто используйте вызов веб-службы, чтобы создать поток для отправки электронных писем и немедленного возврата.
Если вам нужны визуальные индикаторы прогресса, напишите другую конечную точку ajax или страницу aspx, которая будет отображать статус прогресса потока электронной почты.
Есть много способов сделать это, вы должны быть в состоянии найти один с предоставленной информацией.
Пакетирование из ajax, вероятно, будет больше работы, чем вы хотите, и добавляет ненужную сложность (что никогда не бывает хорошо).
это интересно. Я могу добавить это и опубликовать некоторый код.
Хорошо, я вернулся. вот и ты. и веб-интерфейс, и интерфейс AJAX.
Ничто из этого не должно быть готовым продуктом - это всплеск в поддержку истории. согнуть / сложить / шпиндель по желанию.
EmailService.asmx
using System;
using System.ComponentModel;
using System.Threading;
using System.Web.Script.Services;
using System.Web.Services;
namespace EmailSendingWebApplication
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class EmailService : WebService
{
private static EmailSendingProgress _currentProgress;
private static Thread _emailThread;
/// <summary>
///
/// </summary>
/// <param name="criteria">just an example</param>
/// <param name="anotherCriteria">just an example</param>
/// <returns></returns>
[WebMethod]
public EmailSendingProgress SendEmails(string criteria, int anotherCriteria)
{
try
{
if (_currentProgress != null && _emailThread.IsAlive)
{
throw new InvalidOperationException(
"Email batch is already in progress. Wait for completion or cancel");
}
// use your criteria to cue up the emails to be sent.
// .....
// and derive a way for a thread to identify the emails
// i am using a batchid
int batchId = 1000; // contrived
// create a thread
_emailThread = new Thread(ProcessEmails);
_currentProgress = new EmailSendingProgress
{
Status = ProcessState.Starting,
BatchId = batchId
};
// you could use a 'state' object but this process/thread
// is single use/single instance just access _currentProgress
// by the static member
_emailThread.Start();
return _currentProgress;
}
catch (Exception ex)
{
_currentProgress = new EmailSendingProgress
{
Status = ProcessState.Error,
Message = "Error starting process:" + ex.Message
};
}
return _currentProgress;
}
[WebMethod]
public EmailSendingProgress CancelEmailProcess()
{
if (_currentProgress != null && _emailThread.IsAlive)
{
_currentProgress.Cancel = true;
_currentProgress.Message = "Cancelling";
}
return _currentProgress;
}
[WebMethod]
public EmailSendingProgress GetProgress()
{
return _currentProgress;
}
private static void ProcessEmails()
{
// process your emails using the criteria, in this case,
// a batchId
int totalEmails = 100;
int currentEmail = 0;
lock (_currentProgress)
{
_currentProgress.Total = totalEmails;
_currentProgress.Status = ProcessState.Processing;
}
for (; currentEmail < totalEmails; currentEmail++)
{
lock (_currentProgress)
{
if (_currentProgress.Cancel)
{
_currentProgress.Status = ProcessState.Cancelled;
_currentProgress.Message = "User cancelled process.";
break;
}
_currentProgress.Current = currentEmail + 1;
}
try
{
// send your email
Thread.Sleep(100); // lallalala sending email
}
catch (Exception ex)
{
// log the failure in your db
// then check to see if we should exit on error
// or just keep going.
lock (_currentProgress)
{
if (_currentProgress.CancelBatchOnSendError)
{
_currentProgress.Status = ProcessState.Error;
_currentProgress.Message = ex.Message;
break;
}
}
}
}
{
// don't want to obscure state/message from abnormal
// termination..
if (_currentProgress.Status == ProcessState.Processing)
{
_currentProgress.Status = ProcessState.Idle;
_currentProgress.Message = "Processing complete.";
}
}
}
}
public enum ProcessState
{
Idle,
Starting,
Processing,
Cancelled,
Error
}
[Serializable]
public class EmailSendingProgress
{
public int BatchId;
public bool Cancel;
public bool CancelBatchOnSendError;
public int Current;
public string Message;
public ProcessState Status;
public int Total;
}
}
WebFormUI.aspx
<%@ Page Language="C#" %>
<%@ Import Namespace="EmailSendingWebApplication" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
var svc = new EmailService();
UpdateProgress(svc.GetProgress());
}
protected void SendEmailsButton_Click(object sender, EventArgs e)
{
// arbitrary params - modify to suit
string criteria = string.Empty;
int anotherCriteria = 0;
var svc = new EmailService();
UpdateProgress(svc.SendEmails(criteria, anotherCriteria));
}
protected void CancelEmailProcessButton_Click(object sender, EventArgs e)
{
var svc = new EmailService();
UpdateProgress(svc.CancelEmailProcess());
}
private void UpdateProgress(EmailSendingProgress progress)
{
SetButtonState(progress);
DisplayProgress(progress);
}
private void DisplayProgress(EmailSendingProgress progress)
{
if (progress != null)
{
EmailProcessProgressLabel.Text = string.Format("Sending {0} of {1}", progress.Current, progress.Total);
EmailProcessStatusLabel.Text = progress.Status.ToString();
EmailProcessMessageLabel.Text = progress.Message;
}
else
{
EmailProcessProgressLabel.Text = string.Empty;
EmailProcessStatusLabel.Text = string.Empty;
EmailProcessMessageLabel.Text = string.Empty;
}
}
private void SetButtonState(EmailSendingProgress progress)
{
if (progress != null &&
(progress.Status == ProcessState.Starting || progress.Status == ProcessState.Processing))
{
CancelEmailProcessButton.Visible = true;
SendEmailsButton.Visible = false;
}
else
{
CancelEmailProcessButton.Visible = false;
SendEmailsButton.Visible = true;
}
}
protected void RefreshButton_Click(object sender, EventArgs e)
{
// noop just to get postback. you could also use meta headers to refresh the page automatically
// but why?
}
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<br />
EmailProcessStatus:
<asp:Label ID="EmailProcessStatusLabel" runat="server" Text="EmailProcessStatus"></asp:Label>
<br />
EmailProcessProgress:
<asp:Label ID="EmailProcessProgressLabel" runat="server" Text="EmailProcessProgress"></asp:Label>
<br />
EmailProcessMessage:<asp:Label ID="EmailProcessMessageLabel" runat="server" Text="EmailProcessMessage"></asp:Label>
<br />
<br />
<asp:Button ID="SendEmailsButton" runat="server" OnClick="SendEmailsButton_Click"
Text="Send Emails" />
<asp:Button ID="CancelEmailProcessButton" runat="server" OnClick="CancelEmailProcessButton_Click"
Text="Cancel Email Process" />
<br />
<br />
<asp:Button ID="RefreshButton" runat="server" OnClick="RefreshButton_Click" Text="Refresh" />
</div>
</form>
</body>
</html>
AjaxUI.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
//http://www.codeproject.com/Articles/38999/Consuming-ASP-net-WebServices-WCF-Services-and-sta.aspx
var ProcessState = ["Idle", "Starting", "Processing", "Cancelled", "Error"];
function createXHR() {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
else {
throw new Error("Could not create XMLHttpRequest object.");
}
return xhr;
}
function emailAjax(operation, postData, callback) {
var xhr = createXHR();
xhr.open("POST", "EmailService.asmx/" + operation, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
callback(xhr.responseText);
}
};
xhr.setRequestHeader("content-type", "application/json");
xhr.send(postData);
}
function $(id) {
var e = document.getElementById(id);
return e;
}
function startProcess() {
var postData = '{"criteria" : "something", "anotherCriteria" : "1"}';
emailAjax("SendEmails", postData, displayProgress);
}
function cancelProcess() {
emailAjax("CancelEmailProcess", null, displayProgress);
}
function getProgress() {
emailAjax("GetProgress", null, displayProgress);
}
function displayProgress(json) {
eval('var result=' + json + '; var prg=result.d;');
$("EmailProcessMessage").innerHTML = "";
$("EmailProcessStatus").innerHTML = "";
$("EmailProcessProgress").innerHTML = "";
$("CancelEmailProcessButton").style.display = "none";
$("SendEmailsButton").style.display = "none";
if (prg) {
$("EmailProcessMessage").innerHTML = prg.Message;
$("EmailProcessStatus").innerHTML = ProcessState[prg.Status];
$("EmailProcessProgress").innerHTML = "Sending " + prg.Current + " of " + prg.Total;
}
if (prg && (prg.Status == 1 || prg.Status == 2)) {
$("SendEmailsButton").style.display = "none";
$("CancelEmailProcessButton").style.display = "inline";
}
else {
$("CancelEmailProcessButton").style.display = "none";
$("SendEmailsButton").style.display = "inline";
}
}
function init() {
$("SendEmailsButton").onclick = startProcess;
$("CancelEmailProcessButton").onclick = cancelProcess;
// kinda quick but we are only proccing 100 emails for demo
window.setInterval(getProgress, 1000);
}
</script>
</head>
<body onload="init()">
EmailProcessStatus:<span id="EmailProcessStatus"></span><br />
EmailProcessProgress:<span id="EmailProcessProgress"></span><br />
EmailProcessMessage:<span id="EmailProcessMessage"></span><br />
<input type="button" id="SendEmailsButton" value="SendEmails" style="display: none" />
<input type="button" id="CancelEmailProcessButton" value="CancelEmailProcess" style="display: none" />
</body>
</html>