8

我需要通过控制台应用程序异步发送电子邮件。我需要对回调进行一些数据库更新,但我的应用程序在回调代码运行之前退出!

我怎样才能以一种很好的方式阻止这种情况发生,而不是简单地猜测在退出之前要等待多长时间。我会想象异步调用被放置在某种形式的线程中?是否可以检查是否有任何等待被调用?

示例代码

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("system@domain.com", "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
}
4

1 回答 1

8

在退出之前创建一个 ManualResetEvent 调用 WaitOne 。执行最后一个 email/dbupdate 时,在 ManualResetEvent 上调用 Set。


public static void Main(string[] args)
{
    object someArrows = ">>>";
    var users = Repository.GetUsers();
    SmtpClient client = new SmtpClient("Host");
    client.SendCompleted += SendCompletedCallback;
    MailAddress from = new MailAddress("system@domain.com", "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();
    }
}
于 2009-11-19T19:58:45.000 回答