2011-01-31 14 views
8

Verileri bir uzak web hizmetinden aldığımız bir WPF 4.0 uygulaması geliştiriyorum. Web hizmeti, müşterilerine yaklaşık 120'den fazla yöntem sunar. WPF uygulamasından bir web hizmeti çağrısı başarısız olursa, bunu App.Config aracılığıyla yapılandırılabilir n kez tekrar denemeliyim. Bunu nasıl uygularsınız? Bu sorunu ele alan herhangi bir tasarım modeli var mı?C# için istisna durumunda n kere yeniden deneyin?

+4

Bu desenin kendi kendini iyi oluşturmadığını unutmayın. Dört kez yeniden deneyen bir işlev, dört kez yeniden deneyen ve dört kez yeniden deneyen bir işlevi çağıran bir işlev çağırırsa, son işlem 64 kez yeniden denenir. Yeniden denemeler arasında 30 saniye beklerse, kullanıcı hata mesajını beklemek için yarım saat bekler. Bu desene şiddetle tavsiye ederim. Bir şey başarısız olduğunda, * hemen * durdurun, kullanıcıyı anlatın ve yeniden denemeye karar verip vermeyeceğine veya yönlendiricinin fişten çekip çekmediğine karar vermesine izin verin. WPF Apps için –

+0

Ofcourse, yeniden deneme numarası yüksek olmayacak! Yine de, bu arka plan işlemleri gerçekleştiren konsol uygulamaları için oldukça kullanışlıdır. – funwithcoding

+0

Ne yaptın? Bir cevabı işaretlemeyi unutma? –

cevap

6
static T TryNTimes<T>(Func<T> func, int times) 
{ 
    while (times>0) 
    { 
    try 
    { 
     return func(); 
    } 
    catch(Exception e) 
    { 
     if (--times <= 0) 
      throw; 
    } 

    } 
} 
+0

ile katılıyorum Daha çok, ExecuteNTimes değil, TryNTimes olarak adlandırılmalıdır. –

+0

Olmalı, onu düzenleyeceğim. Teşekkürler. –

3

Bu kodu çok uzun bir süre önce, istediğiniz şeye benzer bir şey yapmak için yazdım. İhtiyaçlarınıza uyacak şekilde değiştirilebilir. Genel bekleme yöntemi. Bir işlevden geçirin ve beklenen sonuç iade edilmezse, bekleyin ve ardından X sayısı denemeden sonra tekrar deneyin.

/// <summary> 
    /// Wait for the result of func to return the expeceted result 
    /// </summary> 
    /// <param name="func">Function to execute each cycle</param> 
    /// <param name="result">Desired result returned by func</param> 
    /// <param name="waitInterval">How long to wait (ms) per cycle </param> 
    /// <param name="cycles">How many times to execute func before failing</param> 
    /// <returns>True if desired result was attained. False if specified time runs out before desired result is returned by func</returns> 
    protected static bool WaitForEvent(Func<bool> func, bool result, int waitInterval, int cycles) 
    { 
     int waitCount = 0; 
     while (func() != result) 
     { 
      if (waitCount++ < cycles) 
      { 
       Thread.Sleep(waitInterval); 
      } 
      else 
      { 
       return false; 
      } 
     } 

     return true; 

    } 
+2

Aynı şeyi yazmak üzereydi. Aferin :-) –

+0

Her girişim ile bekleme aralığını artırmak için iyi bir fikir olabilir. – CodesInChaos

+0

ya da (waitCount ++

1
while(retries < maxTries) 
    try 
    { 
     //retryable code here 
     break; 
    } 
    catch(Exception ex) 
    { 
     if(++retries == maxTries) 
     throw; 
     continue; 
    } 

Kesinlikle hiçbir şey fantezi ama işi edeceğiz. Hemen hemen her uygulamada ortak olacak olan ana desen, bir deneyin yakalanmasını içeren ve bir şekilde kontrol edilen bir döngü yapısıdır; Bu, yinelemeli bir çağrı veya yukarıdaki while döngüsü gibi yinelemeli bir döngü olabilir. Başarılı bir denemeden sonra döngüden düzgün bir şekilde çıktığınızdan emin olun ve tekrar deneme sayısını takip edin; Ya başarısızlık sonsuz bir döngüye sebep olur.

+0

Ve eğer bunu bir işleve koyarsa, 120 + yöntem çağrıları için merkezi bir noktası vardır. –

+0

Tanımlanmış herhangi bir bekleme aralığına sahip görünmüyor – NotMe

+1

Bekleme her zaman gerekli değildir; try bloğunda tam olarak ne yapmaya çalıştığınıza bağlıdır. Başarısız bir denemeden sonra uzak bir bilgisayarın temizlendiğinden emin olmanız gerekiyorsa, her şey beklemek demektir. Ancak, Thread.Sleep() dikkatli kullanılmalıdır veya uygulamanın yanıt vermemesine neden olur; Bu, temiz bir şekilde böyle bir şey uygulamak için yapmanız gerekenleri genişletir. – KeithS

0
while(true) 
{ 

try 
{ 
Method(); 
break; 
} 
catch(Exception ex) 
{ 
i++; 
if(i == n) throw ex; 
} 

} 
0

İşte IO paylaşım ihlali sarar benzer bir koddur. Aynı fikir: Daha sonra

/// <summary> 
/// Defines a sharing violation wrapper delegate. 
/// </summary> 
public delegate void WrapSharingViolationsCallback(); 

/// <summary> 
/// Wraps sharing violations that could occur on a file IO operation. 
/// </summary> 
/// <param name="action">The action to execute. May not be null.</param> 
/// <param name="exceptionsCallback">The exceptions callback. May be null.</param> 
/// <param name="retryCount">The retry count.</param> 
/// <param name="waitTime">The wait time in milliseconds.</param> 
public static void WrapSharingViolations(WrapSharingViolationsCallback action, WrapSharingViolationsExceptionsCallback exceptionsCallback, int retryCount, int waitTime) 
{ 
    if (action == null) 
     throw new ArgumentNullException("action"); 

    for (int i = 0; i < retryCount; i++) 
    { 
     try 
     { 
      action(); 
      return; 
     } 
     catch (IOException ioe) 
     { 
      if ((IsSharingViolation(ioe)) && (i < (retryCount - 1))) 
      { 
       bool wait = true; 
       if (exceptionsCallback != null) 
       { 
        wait = exceptionsCallback(ioe, i, retryCount, waitTime); 
       } 
       if (wait) 
       { 
        Thread.Sleep(waitTime); 
       } 
      } 
      else 
      { 
       throw; 
      } 
     } 
    } 
} 

Ve biz (mükemmel burada lambda ifade giysileri) bu şekilde diyoruz:

WrapSharingViolations(() => DoWhatever(...)); 
0

Bir fonksiyonel kullanabilirsiniz bir temsilci ve bir sarıcı statik yöntemine sahip yaklaşım:

class Program 
{ 
    static T Retry<T, TException>(Func<T> thingToTry, int timesToRetry) 
     where TException : Exception 
    { 
     // Start at 1 instead of 0 to allow for final attempt 
     for (int i = 1; i < timesToRetry; i++) 
     { 
      try 
      { 
       return thingToTry(); 
      } 
      catch (TException) 
      { 
       // Maybe: Trace.WriteLine("Failed attempt..."); 
      } 
     } 

     return thingToTry(); // Final attempt, let exception bubble up 
    } 

    static int ServiceCall() 
    { 
     if (DateTime.Now.Ticks % 2 == 0) 
     { 
      throw new InvalidOperationException("Randomly not working"); 
     } 

     return DateTime.Now.Second; 
    } 

    static void Main() 
    { 
     int s = Retry<int, InvalidOperationException>(ServiceCall, 10); 
    } 
} 

Özel istisnalar yakalamak için bunu kullanabilirsiniz (gerekirse daha fazla TEXception genel parametresi ekleyin).

+1

Neden özyinelemeli kullanıyorsunuz? Performans ve bellek sorunları, bir yığın taşma olasılığından bahsetmeyeceğiniz (ortaya koyduğunuz gibi) ortaya çıkacaktır. İyi deneme, ama kötü tasarım IMO. –

+0

İyi nokta. Son zamanlarda çok fazla fonksiyonel kodlama yapıyorum. Sabit. –

+0

Aslında herhangi bir performans ve bellek sorunu yaşamadan önce çok fazla denemelisiniz. –

0

Sen ile bunu mümkün olabilir bir GOTO(gasp)

int count = 0; 
try 
{ 
    TryAgain: 
    // Do something with your web service 
} 
catch(Exception e) 
{ 
    if(count < numberOfAttemptsAllowed) 
    { 
     count++; 
     goto TryAgain; 
    } 
} 

daha iyi bir yolu olabilir eminim ama bu gerekenleri yapabilir.

İlgili konular