2011-03-28 8 views
15

Abbiamo un servizio WCF che stiamo consumando da un'app Web. Il client che stiamo utilizzando è stato generato utilizzando l'opzione "Aggiungi riferimento servizio" di Visual Studio. Poiché si tratta di un'app Web e dal momento che la natura dell'app probabilmente porterà a sessioni relativamente brevi, abbiamo deciso di creare un'istanza del client quando un utente effettua l'accesso e di mantenerlo per tutta la durata della sessione, quindi gestire lo smaltimento di esso durante la sessione.Gestire il client WCF persistente che entra nello stato di errore

Questo mi porta alla mia domanda: stiamo cercando di decidere il modo migliore per gestire il canale del cliente che entra in uno stato Fault. Dopo la ricerca attorno ad alcuni, siamo arrivati ​​fino a questo:

if(client.State = CommuncationState.Faulted) 
{ 
    client = new Client(); 
} 

try 
{ 
    client.SomeMethod(); 
} 
catch //specific exceptions left out for brevity 
{ 
    //logging or whatever we decide to do 
    throw; 
} 

Questo, tuttavia, non funziona a causa del fatto che, almeno nel nostro caso, anche se il servizio non è attivo il cliente indicherà il Open stato finché non si tenta effettivamente di effettuare una chiamata che lo utilizza, a quel punto entra quindi nello stato Faulted.

Quindi questo ci lascia fare qualcos'altro. Un'altra opzione che abbiamo trovato è stata:

try 
{ 
    client.SomeMethod(); 
} 
catch 
{ 
    if(client.State == CommunicationState.Faulted) 
    { 
     //we know we're faulted, try it again 
     client = new Client(); 
     try 
     { 
      client.SomeMethod(); 
     } 
     catch 
     { 
      throw; 
     } 
    } 
    //handle other exceptions 
} 

Ma quell'odore. Ovviamente, potremmo evitarlo usando un nuovo client e smaltendolo per ogni chiamata. Sembra inutile, ma se è la strada giusta, immagino che sia quello che opteremo per. Quindi qual è il modo migliore per gestire con garbo la determinazione se il client è in uno stato di errore e quindi fare qualcosa al riguardo? Dovremmo davvero ricevere un nuovo cliente per ogni chiamata?

Un'altra cosa da tenere a mente: l'istanziazione del client e tutto questo controllo e gestione avviene in una classe wrapper per il client. Se lo facciamo nel modo in cui intendevamo, è trasparente per l'app stessa: effettuare chiamate e gestire le eccezioni non richiede alcun codice speciale.

+0

Che cosa sta causando al client di entrare in uno stato di errore? Sono sempre stato in grado di avere un servizio WCF che restituisce un errore normalmente e il cliente può continuare a fare affari. Il server non risponde o qualcosa del genere? – Tridus

+0

In questo caso, stiamo utilizzando l'appartenenza a ASP.NET e ci siamo imbattuti in esso superando l'attributo "userIsOnlineTimeWindow". Ovviamente in tal caso avrebbe senso semplicemente reindirizzare l'utente alla pagina di accesso, ma stiamo cercando di essere sicuri di essere pronti per qualsiasi altra situazione in cui potremmo entrare in uno stato di errore. – Zannjaminderson

risposta

18

Per rispondere alla tua domanda, è possibile gestire il Faulted event della proprietà ChannelFactory in questo modo:

client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted); 

che dovrebbe consentire di eseguire qualsiasi registrazione/cleanup che è necessario fare.

Come raccomandazione generale, non lasciare il canale aperto per la durata della sessione, quindi assicurati di aver chiuso il canale correttamente (interruzione su eccezione) dopo aver finito con esso.

Inoltre, se possibile, considerare NON utilizzare Visual Studio Aggiungi riferimento al servizio o per lo meno pulire il codice/config che genera.Raccomando che se si desidera utilizzare un'implementazione proxy, crearne una propria derivando da ClientBase o utilizzare un'implementazione ChannelFactory. Poiché menzioni una classe wrapper, ti consiglio di utilizzare ChannelFactory e gestire lo Faulted event per le tue esigenze di pulizia.

+0

Grazie per la risposta. Ho visto molte persone parlare dell'apertura e della chiusura di un canale client per ogni chiamata e mi chiedo quali sono le ragioni per farlo in quel modo? – Zannjaminderson

+2

Il motivo è che è possibile avere solo così tante chiamate simultanee e sessioni simultanee al proprio servizio. Pertanto, se ogni sessione della tua app Web prevede una sessione aperta, si raggiungerà rapidamente il limite predefinito e sarà necessario cambiarlo e le prestazioni ne risentiranno. Controllare questo per ulteriori informazioni sull'ottimizzazione di queste (e altre) impostazioni: http://msdn.microsoft.com/en-us/library/ee377061(v=bts.10).aspx – BrandonZeider

+0

Grazie. Ho letto un po 'di più su ChannelFactory e sembra che ci stiamo muovendo in quella direzione. – Zannjaminderson

11

Prova la gestione dell'evento .Faulted sul proxy client, ad esempio:

((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted); 

private void client_Faulted(object sender, EventArgs e) 
{ 
    client = new Client(); 
    ((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted); 
} 

Dovrebbe scattare l'istantanea dei guasti del canale, dandovi la possibilità di riaprirlo.

Si dovrebbe comunque anche avvolgere ogni chiamata a un metodo client in un blocco try-catch, e forse anche avvolgere che in un ciclo while() che ritenta la chiamata n volte, poi registra un fallimento. EG:

bool succeeded = false; 
int triesLeft = 3; 
while (!succeeded && triesLeft > 0) 
{ 
    triesLeft--; 
    try 
    { 
     client.SomeMethod(); 
     succeeded = true; 
    } 
    catch (exception ex) 
    { 
     logger.Warn("Client call failed, retries left: " + triesLeft; 
    } 
} 
if (!succeeded) 
    logger.Error("Could not call web service"); 

Nel mio codice sono andato al punto di utilizzare un ManualResetEvent per bloccare il ciclo while() fino a quando il gestore client_Faulted manifestazione ha avuto la possibilità di ricreare il client proxy.

+1

Che dire dell'annullamento dell'iscrizione dall'evento 'Faulted'? dove dovrebbe essere fatto? – itsho

Problemi correlati