c# - recibió - Cómo manejar excepciones WCF(lista consolidada con código)
manejo de excepciones personalizadas c# (4)
Comencé un proyecto en Codeplex que tiene las siguientes características
- Permite la reutilización eficiente del proxy del cliente
- Limpia todos los recursos, incluyendo EventHandlers
- Opera en canales dúplex
- Opera en servicios por llamada
- Admite el constructor de configuración, o de fábrica
http://smartwcfclient.codeplex.com/
Es un trabajo en progreso, y está muy comentado. Agradeceré cualquier comentario sobre mejorarlo.
Uso de muestra cuando está en modo instancia:
var reusableSW = new LC.Utils.WCF.ServiceWrapper<IProcessDataDuplex>(channelFactory);
reusableSW.Reuse(client =>
{
client.CheckIn(count.ToString());
});
reusableSW.Dispose();
Estoy tratando de extender esta respuesta en SO para hacer que un cliente de WCF vuelva a intentar fallas de red transitorias y maneje otras situaciones que requieren un reintento como la caducidad de la autenticación.
Pregunta:
¿Cuáles son las excepciones de WCF que deben manejarse y cuál es la forma correcta de manejarlas?
Aquí hay algunas técnicas de muestra que espero ver en lugar de o además de proxy.abort()
:
- Demora X segundos antes de volver a intentar
- Cierre y vuelva a crear un nuevo () cliente WCF. Deshágase del anterior.
- No vuelva a intentar y vuelva a generar este error
- Reintentar N veces, luego tirar
Dado que es poco probable que una persona sepa todas las excepciones o formas de resolverlas, comparta lo que sabe. Agregaré las respuestas y los enfoques en el siguiente ejemplo de código.
// USAGE SAMPLE
//int newOrderId = 0; // need a value for definite assignment
//Service<IOrderService>.Use(orderService=>
//{
// newOrderId = orderService.PlaceOrder(request);
//}
/// <summary>
/// A safe WCF Proxy suitable when sessionmode=false
/// </summary>
/// <param name="codeBlock"></param>
public static void Use(UseServiceDelegateVoid<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
catch (CommunicationObjectAbortedException e)
{
// Object should be discarded if this is reached.
// Debugging discovered the following exception here:
// "Connection can not be established because it has been aborted"
throw e;
}
catch (CommunicationObjectFaultedException e)
{
throw e;
}
catch (MessageSecurityException e)
{
throw e;
}
catch (ChannelTerminatedException)
{
proxy.Abort(); // Possibly retry?
}
catch (ServerTooBusyException)
{
proxy.Abort(); // Possibly retry?
}
catch (EndpointNotFoundException)
{
proxy.Abort(); // Possibly retry?
}
catch (FaultException)
{
proxy.Abort();
}
catch (CommunicationException)
{
proxy.Abort();
}
catch (TimeoutException)
{
// Sample error found during debug:
// The message could not be transferred within the allotted timeout of
// 00:01:00. There was no space available in the reliable channel''s
// transfer window. The time allotted to this operation may have been a
// portion of a longer timeout.
proxy.Abort();
}
catch (ObjectDisposedException )
{
//todo: handle this duplex callback exception. Occurs when client disappears.
// Source: https://stackoverflow.com/questions/1427926/detecting-client-death-in-wcf-duplex-contracts/1428238#1428238
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
EDITAR: Parece haber algunas ineficiencias con el cierre y la reapertura del cliente varias veces. Estoy explorando soluciones aquí y actualizaré y expandiré este código si se encuentra uno. (O si David Khaykin publica una respuesta, la marcaré como aceptada)
Después de jugar con esto durante algunos años, el siguiente código es mi estrategia preferida ( después de ver esta publicación del blog desde la máquina de retorno ) para tratar con los reintentos de WCF y manejar excepciones.
Investigué cada excepción, lo que querría hacer con esa excepción, y noté un rasgo común; cada excepción que necesitaba un "reintento" heredado de una clase base común. También noté que todas las excepciones de permFail que ponían al cliente en un estado no válido también provenían de una clase base compartida.
El siguiente ejemplo atrapa todas las excepciones de WCF que un cliente podría atravesar, y es extensible a sus propios errores de canal personalizados.
Ejemplo de uso del cliente WCF
Una vez que genere su proxy del lado del cliente, esto es todo lo que necesita para implementarlo.
Service<IOrderService>.Use(orderService=>
{
orderService.PlaceOrder(request);
}
ServiceDelegate.cs
Agregue este archivo a su solución. No se necesitan cambios en este archivo, a menos que desee modificar el número de reintentos o las excepciones que desea manejar.
public delegate void UseServiceDelegate<T>(T proxy);
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = null;
bool success = false;
Exception mostRecentEx = null;
int millsecondsToSleep = 1000;
for(int i=0; i<5; i++) // Attempt a maximum of 5 times
{
// Proxy cann''t be reused
proxy = (IClientChannel)_channelFactory.CreateChannel();
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
break;
}
catch (FaultException customFaultEx)
{
mostRecentEx = customFaultEx;
proxy.Abort();
// Custom resolution for this app-level exception
Thread.Sleep(millsecondsToSleep * (i + 1));
}
// The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
catch (ChannelTerminatedException cte)
{
mostRecentEx = cte;
proxy.Abort();
// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}
// The following is thrown when a remote endpoint could not be found or reached. The endpoint may not be found or
// reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
catch (EndpointNotFoundException enfe)
{
mostRecentEx = enfe;
proxy.Abort();
// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}
// The following exception that is thrown when a server is too busy to accept a message.
catch (ServerTooBusyException stbe)
{
mostRecentEx = stbe;
proxy.Abort();
// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}
catch (TimeoutException timeoutEx)
{
mostRecentEx = timeoutEx;
proxy.Abort();
// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}
catch (CommunicationException comException)
{
mostRecentEx = comException;
proxy.Abort();
// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}
catch(Exception e)
{
// rethrow any other exception not defined here
// You may want to define a custom Exception class to pass information such as failure count, and failure type
proxy.Abort();
throw e;
}
}
if (success == false && mostRecentEx != null)
{
proxy.Abort();
throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
}
}
}
Los enlaces a continuación pueden ayudar a manejar Excepciones de WCF:
tenemos un cliente WCF que se ocupa de casi cualquier tipo de falla en el servidor. La lista de Capturas es muy larga, pero no tiene que ser así. Si observa detenidamente, verá que muchas excepciones son definiciones secundarias de la Clase de excepción (y algunas otras clases).
Por lo tanto, puede simplificar mucho las cosas si lo desea. Dicho esto, aquí hay algunos errores típicos que detectamos:
Tiempo de espera del servidor
Servidor muy ocupado
Servidor no disponible.