2009-11-19 6 views
8

Мне нужно отправить электронную почту асинхронно через консольное приложение. Мне нужно сделать некоторые обновления БД при обратном вызове, но мое приложение выходит до запуска кода обратного вызова!SmtpClient.SendAsync - Как остановить приложение, выходящее до запуска обратного вызова?

Как я могу прекратить это происходить красивым способом, а не просто угадывая как долго ждать до выхода. Я бы предположил, что вызовы Async попадают в какую-то форму потока? Можно ли проверить, ожидаются ли какие-либо вызовы?

Пример кода

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 
} 

ответ

8

Создание ManualResetEvent вызова WaitOne один его на ней перед выходом. Когда выполняется последнее письмо/dbupdate, вызовите Set в 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

Привет, вы могли бы предоставить образец того, как вы бы посоветовали это сделать? Я обновил свой ответ, чтобы показать вам пример – James

+0

Вы устанавливаете SendCompleted + = SendCompletedCallback, но затем в цикле вы назначаете SendCompleted для делегата? Означает ли это, что код делегата запускается, а затем вызывается SendCompletedCallback? – James

+0

Да. Но учтите, что порядок, в котором вызываются обратные вызовы, не гарантируется. Таким образом, SendCompleted может либо вызвать SendCompletedCallback 1st, либо мой делегат 1st. –