2011-03-28 22 views
15

Tenemos un servicio WCF que estamos consumiendo desde una aplicación web. El cliente que estamos utilizando se generó utilizando la opción "Agregar referencia de servicio" de Visual Studio. Dado que es una aplicación web, y dado que la naturaleza de la aplicación es probable que conduzca a sesiones relativamente cortas, hemos optado por crear una instancia del cliente cuando un usuario inicia sesión y lo mantiene durante toda la sesión, luego maneje su eliminación cuando la sesión haya terminado.Manejar al cliente WCF persistente ingresando el estado fallado

Esto me lleva a mi pregunta: estamos tratando de decidir la mejor manera de manejar el canal del cliente entrando en un estado Faulted. Después de buscar en torno a algunos, que hemos llegado con esto:

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; 
} 

Esto, sin embargo, no funciona debido al hecho de que, al menos en nuestro caso, incluso si el servicio se ha reducido el cliente mostrará el Open estado hasta que realmente intenta hacer una llamada que lo usa, en ese momento entra en el estado Faulted.

Esto nos deja para hacer otra cosa. Otra opción que se nos ocurrió fue:

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 
} 

Pero eso huele mal. Obviamente, podríamos evitar esto usando un nuevo cliente y disponiéndolo para cada llamada. Parece innecesario, pero si es el camino correcto, entonces supongo que eso es lo que optaremos. Entonces, ¿cuál es la mejor manera de manejar con elegancia si el cliente está en un estado de falla y luego hacer algo al respecto? ¿De verdad deberíamos obtener un nuevo cliente para cada llamada?

Otra cosa a tener en cuenta: la creación de instancias del cliente y todas estas comprobaciones y gestiones se realizan en una clase contenedora para el cliente. Si hacemos esto de la forma en que lo intentamos, es transparente para la aplicación en sí misma: realizar llamadas y manejar excepciones de ellas no requiere ningún código especial allí.

+0

¿Qué causa que el cliente entre en un estado con falla? Siempre he podido hacer que un servicio WCF devuelva un error normalmente y el cliente puede continuar con su negocio. ¿El servidor no responde o algo así? – Tridus

+0

En este caso, estamos usando membresía ASP.NET, y nos encontramos con ella al exceder el atributo "userIsOnlineTimeWindow". Obviamente, en ese caso, tendría sentido redirigir al usuario a la página de inicio de sesión, pero estamos intentando asegurarnos de que estamos preparados para cualquier otra situación en la que podamos ponernos en estado de error. – Zannjaminderson

Respuesta

18

Para responder a su pregunta, puede manejar el Faulted event de la propiedad ChannelFactory así:

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

que debe permitir llevar a cabo cualquier explotación y/o de limpieza que lo que necesita hacer.

Como recomendación general, no debe dejar el canal abierto mientras dure la sesión, por lo tanto, asegúrese de cerrar correctamente el canal (cancelando la excepción) después de que haya terminado con el mismo.

Además, si es posible, considere NO utilizar Visual Studio Add Service Reference, o al menos limpiar el código/config que genera.Recomiendo que, si desea utilizar una implementación de proxy, cree la suya derivando de ClientBase, o use una implementación de ChannelFactory. Como mencionas una clase contenedora, te recomendaría que uses ChannelFactory y manejes el Faulted event para tus necesidades de limpieza.

+0

Gracias por la respuesta. He visto a mucha gente hablar acerca de abrir y cerrar un canal de cliente para cada llamada y me pregunto cuáles son las razones para hacerlo de esa manera. – Zannjaminderson

+2

La razón es que solo puede tener tantas llamadas concurrentes y sesiones simultáneas para su servicio. Entonces, si cada sesión en su aplicación web tiene una sesión abierta, rápidamente alcanzará el límite predeterminado y deberá cambiarla, y el rendimiento se verá afectado. Consulte esto para obtener más información sobre la optimización de estas (y otras) configuraciones: http://msdn.microsoft.com/en-us/library/ee377061(v=bts.10).aspx – BrandonZeider

+0

Gracias. He estado leyendo un poco más en ChannelFactory y parece que nos moveremos en esa dirección. – Zannjaminderson

11

Trate de controlar el evento .Faulted en el proxy de cliente, por ejemplo:

((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); 
} 

Debe activar el instante en que los fallos de la vía, que le da la oportunidad de volver a abrirlo.

También debería ajustar cada llamada a un método client en un bloque try-catch, y quizás incluso envolverlo en un bucle while() que reintenta la llamada n veces, luego registra un error. 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"); 

En mi código que he ido tan lejos como para usar un ManualResetEvent para bloquear el bucle while() hasta que el manejador client_Faulted evento ha tenido la oportunidad de volver a crear el proxy client.

+1

¿Qué hay de cancelar la suscripción al evento 'Faulted'? ¿Dónde debería hacerse? – itsho

Cuestiones relacionadas