2015-04-13 8 views
8

SmtpClient kullanarak e-posta gönderdiğim bir windows form uygulamasına sahibim. Uygulamadaki diğer async işlemleri async/wait'i kullanıyor ve postayı gönderirken tutarlı olmak isterim.Bir async görevini beklemeli ve aynı yöntemde bir modal formunu nasıl göstermeli?

Postayı gönderirken ve SendMailAsync'i form ile birleştirerek, bir iptal düğmesiyle kalıcı bir iletişim kutusu görüntüledim.Görüntüsü, işin zorlaştığı yerdir çünkü gönderenin beklemesi engellenir ve bu nedenle ShowDialog olur. Şu anki yaklaşımım şu şekildedir, ama dağınık görünüyor, buna daha iyi bir yaklaşım var mı?

private async Task SendTestEmail() 
{ 
    // Prepare message, client, and form with cancel button 
    using (Message message = ...) 
    { 
    SmtpClient client = ... 
    CancelSendForm form = ... 

    // Have the form button cancel async sends and 
    // the client completion close the form 
    form.CancelBtn.Click += (s, a) => 
    { 
     client.SendAsyncCancel(); 
    }; 
    client.SendCompleted += (o, e) => 
    { 
     form.Close(); 
    }; 

    // Try to send the mail 
    try 
    { 
     Task task = client.SendMailAsync(message); 
     form.ShowDialog(); 
     await task; // Probably redundant 

     MessageBox.Show("Test mail sent", "Success"); 
    } 
    catch (Exception ex) 
    { 
     string text = string.Format(
      "Error sending test mail:\n{0}", 
      ex.Message); 
     MessageBox.Show(text, "Error"); 
    } 
    } 
+0

oldukça düzgün bir çözüm gibi görünüyor. 'Beklemenin' gerekli olmadığına, ancak güzel ve okunabilir olduğuna emin olursunuz. – Gusdor

+0

Teşekkürler, eklediğiniz ve daha sonra kaldırdığınız çözümü denedim. Ancak form.Show (bu) (ana formun olduğu yer) artık ana formu ana form olarak kabul etmiyor, bu yüzden de yukarıdaki gibi bir açıklama yapmadığım form.StartPosition = FormStartPosition.CenterScreen davranışını kaybediyorum. Daha sonra üst formu geçerli form için üst ayarlamak için girişimi ve form üstte bir istisna atar.TopLevel salt okunur (tavşan delik! :)) – FlintZA

+0

Ben yanlış oldu çünkü cevabı kaldırdı :) – Gusdor

cevap

10

Ben Form.Shown olay işleme ve oradan e-posta göndererek ele alacak. Eşzamansız olarak çalışacağından, "etrafta" ShowDialog 'un engellenmesiyle ilgili endişelenmenize gerek yoktur ve formu kapatıp başarı veya başarısızlık mesajını göstermeyi senkronize etmenin biraz daha temiz bir yoluna sahip olursunuz. Mevcut SendTestEmail uygulaması hakkında

form.Shown += async (s, a) => 
{ 
    try 
    { 
     await client.SendMailAsync(message); 
     form.Close(); 
     MessageBox.Show("Test mail sent", "Success"); 
    } 
    catch(Exception ex) 
    { 
     form.Close(); 
     string text = string.Format(
      "Error sending test mail:\n{0}", 
      ex.Message); 
     MessageBox.Show(text, "Error"); 
    } 
}; 

form.ShowDialog(); 
+1

Bu gerçekten okunabilirliğe eklemek için çok fazla şey yapmıyor. _does_ yapmak, orjinal kodda bulunan yarış durumu hatasını düzeltmektir, burada eğer iletişim diyalogdan önce tamamlandıysa (küçük bir risk, ancak uygulama tarafından mantıksal olarak engellenmemişse), 'Kapat() yöntem yanlış zamanda çağrılır (iletişim kutusu gösterilmeden önce). 'try' /' catch' kullanımı '' Wawn' olay işleyicisine '' beklet '' tarafından atılan istisnaları yakalamak için taşınabilir. –

+0

Tüm noktalarla katılıyorum. Güncellenen kodum - e-posta gönderme hatalarıyla uğraşmak için try/catch görünüyor ve kesinlikle olay işleyicisine ait. –

+0

Teşekkürler, haklısın, bu, orijinal listelemedeki belirli sorun için daha iyi bir yaklaşım. Ayrıca, @Peter Duniho'nun bahsettiği başarısız çağrı sorununun küçük şansını ele almasını sevdim. Düzenlemelerinizden sonra en az orijinali kadar okunabilir olduğunu düşünüyorum. – FlintZA

1

Bir sorgulanabilir şey bir Task döndürür rağmen o, aslında senkronize olarak olmasıdır. Bu nedenle, yalnızca görev tamamlandığında geri döner, çünkü ShowDialog senkrondır (doğal olarak iletişim kutusunun kalıcı olması nedeniyle).

Bu biraz yanıltıcı olabilir.

var sw = new Stopwatch(); 
sw.Start(); 
var task = SendTestEmail(); 
while (!task.IsCompleted) 
{ 
    await WhenAny(Task.Delay(500), task); 
    StatusBar.Text = "Lapse, ms: " + sw.ElapsedMilliseconds; 
} 
await task; 

Bu olabilir kolayca Task.Yield ile ele, yeni (iç içe) kalıcı iletişim mesajı döngü uyumsuz sürdürmesine imkan verecektir:

public static class FormExt 
{ 
    public static async Task<DialogResult> ShowDialogAsync(
     Form @this, CancellationToken token = default(CancellationToken)) 
    { 
     await Task.Yield(); 
     using (token.Register(() => @this.Close(), useSynchronizationContext: true)) 
     { 
      return @this.ShowDialog(); 
     } 
    } 
} 
Örneğin, aşağıdaki kod beklenen şekilde işe yaramaz

Sonra bu (denenmemiş) gibi bir şey yapabileceğini:

private async Task SendTestEmail(CancellationToken token) 
{ 
    // Prepare message, client, and form with cancel button 
    using (Message message = ...) 
    { 
     SmtpClient client = ... 
     CancelSendForm form = ... 

     // Try to send the mail 
     var ctsDialog = CancellationTokenSource.CreateLinkedTokenSource(token); 
     var ctsSend = CancellationTokenSource.CreateLinkedTokenSource(token); 
     var dialogTask = form.ShowDialogAsync(ctsDialog.Token); 
     var emailTask = client.SendMailExAsync(message, ctsSend.Token); 
     var whichTask = await Task.WhenAny(emailTask, dialogTask); 
     if (whichTask == emailTask) 
     { 
      ctsDialog.Cancel(); 
     } 
     else 
     { 
      ctsSend.Cancel(); 
     } 
     await Task.WhenAll(emailTask, dialogTask); 
    } 
} 

public static class SmtpClientEx 
{ 
    public static async Task SendMailExAsync(
     SmtpClient @this, MailMessage message, 
     CancellationToken token = default(CancellationToken)) 
    { 
     using (token.Register(() => 
      @this.SendAsyncCancel(), useSynchronizationContext: false)) 
     { 
      await @this.SendMailAsync(message); 
     } 
    } 
} 
+0

@ScottChamberlain, "SendAsyncCancel" i işaretsiz bıraktığınız halde, SendMailAsync ile hala kullanılabilir olduğunu belirttiğiniz için teşekkür ederiz. Yukarıdaki kod buna dayanır. Yine de, muhtemelen [bu bir] gibi özel bir uygulama ile yapışabilirim (http://stackoverflow.com/a/28445791/1768303). – Noseratio

+1

Teşekkürler, bu cevaptan asn/bekletme koduyla ilgili pek çok yeni şey öğrendim. @Todd Menier'in cevabını seçmeyi seçtim çünkü bence posta gönderme kodunu formun Show işleyicisine taşımam gerektiğini düşünüyorum. Bence sizinki orijinal soruya (daha genel) bir başlık daha iyi bir cevaptır, bu yüzden başlığı sorunla sınırlamak için güncelledim. Ayrıca, async olmadığına dair bu not için teşekkürler, haklısın ve Todd'un yanıtını kullanıyorum, muhtemelen async değiştiricisini kaldıracağım. – FlintZA

İlgili konular