2010-02-14 37 views
0

我被要求在asp.net上開發一個自動郵件發送程序。它應該發送,例如從數據庫中讀取地址的5000封電子郵件。它肯定會陷入請求超時困難。所以看來我必須將其轉換爲Windows應用程序。但我想知道,如果這個網絡應用程序Ajax化會有所幫助。如果我編寫Web服務,並且我的Web應用程序一次將郵件地址以50個列表的形式發送。完成後,發送下一個50,依此類推。這有助於解決http請求超時問題嗎?郵件發送程序和請求超時

+0

尖刺和測試 - 時間已過去40分鐘。 – 2010-02-14 13:47:57

+0

也看到http://www.codeproject.com/Articles/38999/Consuming-ASP-net-WebServices-WCF-Services-and-sta.aspx – 2010-02-14 13:55:33

回答

1

使用web服務端點發送您的電子郵件是一個好主意,不管你從一個aspx類或從客戶端的JavaScript調用它。

只需使用Web服務調用生成一個線程來發送郵件並立即返回。

如果你想可見的進度提示,然後再寫AJAX端點或aspx頁面將顯示的電子郵件線程的進展情況。

有很多方法可以實現這一點,你應該能夠拿出一個給出的信息。從阿賈克斯

配料可能將是更多的工作比你想做的事,並增加了不必要的複雜性(這是從來沒有一件好事)。

這很有趣。我可能會飆升併發布一些代碼。

好的,我回來了。在這裏你去。 webform ui和ajax ui。

這些都不是爲了成品 - 是一個支持故事的秒殺。彎曲/摺疊/主軸隨意。

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" /> 
     &nbsp;<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> 
+0

感謝很多提前:) – emre 2010-02-14 12:00:44

+0

源項目是在這裏: http://skysanders.codeplex.com/SourceControl/list/changesets – 2010-02-14 14:04:22

0

所以用戶會公頃在瀏覽器窗口打開之前,所有的電子郵件都被髮送出去了嗎?聽起來不太好。我會在Windows上使用守護進程或由cron運行的簡單腳本(並檢查數據庫是否有要發送的內容)解決此問題,我希望您可以做類似的事情(編寫Windows服務等)。這是一個純粹的服務器端的任務,我覺得ajaxifying它表明了Web應用程序的作者並不能夠使它以更好的方式,它甚至可以讓你的Web應用程序要在thedailywtf.com提到:)

+0

哈哈哈真的嗎? :) – emre 2010-02-14 12:02:33

+0

好的,messa,應該在dwtf中提到我的應用程序? ;-) – 2010-02-14 13:47:21