2009-11-19 3 views
8

Devo inviare email in modo asincrono tramite un'applicazione console. Ho bisogno di fare alcuni aggiornamenti del DB sul callback ma la mia applicazione sta uscendo prima che il codice di callback venga eseguito!SmtpClient.SendAsync - Come interrompere l'uscita dall'applicazione prima che venga attivata la richiamata?

Come posso evitare che ciò accada in modo positivo anziché semplicemente indovinando per quanto tempo attendere prima di uscire. Immagino che le chiamate Async vengano inserite in una qualche forma di thread? È possibile verificare se qualcuno è in attesa di essere chiamato?

codice di esempio

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e) 
{ 
    // Get the unique identifier for this asynchronous operation. 
    String token = (string) e.UserState; 
    if (e.Cancelled) 
    { 
     Console.WriteLine("[{0}] Send canceled.", token); 
    } 
    if (e.Error != null) 
    { 
     Console.WriteLine("[{0}] {1}", token, e.Error.ToString()); 
    } 
    else 
    { 
     // update DB 
     Console.WriteLine("Message sent."); 
    } 
} 

public static void Main(string[] args) 
{ 
    var users = Repository.GetUsers(); 
    SmtpClient client = new SmtpClient("Host"); 
    client.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback); 
    MailAddress from = new MailAddress("[email protected]", "System", Encoding.UTF8); 
    foreach (var user in users) 
    { 
     MailAddress to = new MailAddress(user.Email); 
     MailMessage message = new MailMessage(from, to); 
     message.Body = "This is a test"; 
     message.BodyEncoding = System.Text.Encoding.UTF8; 
     message.Subject = "test message 1" + someArrows; 
     message.SubjectEncoding = System.Text.Encoding.UTF8; 
     string userState = String.Format("Message for user id {0}", user.ID); 
     client.SendAsync(message, userState); 
     message.Dispose(); 
    } 

    // need to wait here until I have received a callback for each message 
    // otherwise the application will exit 
} 

risposta

8

creare una chiamata ManualResetEvent WaitOne uno che su di esso prima di uscire. Quando viene eseguita l'ultima email/dbupdate, chiamare Set su ManualResetEvent.


public static void Main(string[] args) 
{ 
    object someArrows = ">>>"; 
    var users = Repository.GetUsers(); 
    SmtpClient client = new SmtpClient("Host"); 
    client.SendCompleted += SendCompletedCallback; 
    MailAddress from = new MailAddress("[email protected]", "System", Encoding.UTF8); 
    int numRemaining = users.Length; 
    using(ManualResetEvent waitHandle = new ManualResetEvent(numRemaining == 0)) 
    { 
     object numRemainingLock = new object(); 
     foreach(var user in users) 
     { 
      MailAddress to = new MailAddress(user.Email); 
      MailMessage message = new MailMessage(from, to); 
      try 
      { 
       message.Body = "This is a test"; 
       message.BodyEncoding = System.Text.Encoding.UTF8; 
       message.Subject = "test message 1" + someArrows; 
       message.SubjectEncoding = System.Text.Encoding.UTF8; 
       string userState = String.Format("Message for user id {0}", user.ID); 
       client.SendCompleted += delegate 
       { 
        lock(numRemainingLock) 
        { 
         if(--numRemaining == 0) 
         { 
          waitHandle.Set(); 
         } 
        } 
       }; 
       client.SendAsync(message, userState); 
      } 
      catch 
      { 
       message.Dispose(); 
       throw; 
      } 
     } 
     waitHandle.WaitOne(); 
    } 
} 
+0

Ciao, potresti fornire un esempio di come consiglieresti che ciò avvenga? Ho aggiornato la mia risposta per mostrarti un esempio – James

+0

Stai impostando SendCompleted + = SendCompletedCallback, ma poi nel ciclo stai assegnando SendCompleted a un delegato? Significa che il codice delegato viene eseguito e quindi viene chiamato SendCompletedCallback? – James

+0

Sì. Tuttavia, l'ordine in cui vengono richiamati i callback non è garantito. Quindi, SendCompleted può chiamare SendCompletedCallback 1st o il mio delegato 1st. –