294 votos

¿Cuál es la mejor solución para el cliente WCF `using` bloquear tema?

Me gusta crear instancias de mi servicio WCF los clientes dentro de un using bloque, ya que es casi la norma en forma de utilizar los recursos que implementan IDisposable:

using (var client = new SomeWCFServiceClient()) 
{
    //Do something with the client 
}

Pero, como se indica en este artículo de MSDN, la inclusión de un cliente de WCF en un using bloque podría enmascarar los errores que resultan en que el cliente se quede en una falla del estado (como un tiempo de espera o problema de comunicación). Larga historia corta, cuando Dispose() es llamado, el cliente del método Close() de los incendios, pero arroja un error porque es una falla del estado. El original de la excepción, a continuación, enmascarado por la segunda excepción. No es bueno.

La solución sugerida en el artículo de MSDN es evitar completamente el uso de un using bloque, y en lugar de crear instancias de sus clientes y utilizar algo como esto:

try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

En comparación con el using bloque, creo que eso es feo. Y una gran cantidad de código a escribir cada vez que necesita un cliente.

Por suerte, he encontrado un par de otras soluciones, como la que tiene este IServiceOriented. Empezar con:

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 = (IClientChannel)_channelFactory.CreateChannel(); 
        bool success = false; 
        try 
        { 
            codeBlock((T)proxy); 
            proxy.Close(); success = true; 
        } 
        finally 
        { 
            if (!success) 
            { 
                proxy.Abort(); 
            } 
        } 
     } 
} 

Lo que permite:

Service<IOrderService>.Use(orderService => 
{ 
    orderService.PlaceOrder(request); 
}); 

Eso no es malo, pero yo no creo que sea tan expresiva y fácil de entender como el using bloque.

La solución actualmente estoy tratando de utilizar la primera vez que leí acerca de blog.davidbarret.net. Básicamente, se reemplaza el cliente Dispose() método dondequiera que se use. Algo como:

public partial class SomeWCFServiceClient : IDisposable
{
    void IDisposable.Dispose() 
    {
        if (this.State == CommunicationState.Faulted) 
        {
            this.Abort();
        } 
        else 
        {
            this.Close();
        }
    }
}

Este parece ser capaz de permitir la using bloque de nuevo sin el peligro de aplicar una máscara a un fallo del estado de excepción.

Así, hay otros errores que tengo que mirar hacia fuera para el uso de estas soluciones? Nadie ha llegado a algo mejor?

105voto

Marc Gravell Puntos 482669

En realidad, aunque me escribió en su blog (ver Lucas respuesta), creo que esta (ver enlace alternativo) es mejor que mi IDisposable contenedor. Típico código:

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
}

(edit por los comentarios)

Desde Use devuelve void, la forma más sencilla de manejar los valores de retorno es a través de un capturados variable:

int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
  {
    newOrderId = orderService.PlaceOrder(request);
  });
Console.WriteLine(newOrderId); // should be updated

75voto

Matt Davis Puntos 22019

Dada la opción entre la solución propugnada por IServiceOriented.com y la solución defendida por David de Barret blog, yo prefiero la simplicidad que ofrece reemplazando el cliente del método Dispose (). Esto me permite seguir utilizando el uso de(a) declaración de como sería de esperar con un objeto desechable. Sin embargo, como @Brian señaló, esta solución contiene una condición de carrera en el que el Estado puede no ser criticado cuando está marcada, pero podría ser por el tiempo de Cierre() es llamado, en cuyo caso el CommunicationException todavía se produce.

Así que, para evitar esto, he empleado una solución que combina lo mejor de ambos mundos.

void IDisposable.Dispose()
{
    bool success = false;
    try {
        if (State != CommunicationState.Faulted) {
            Close();
            success = true;
        }
    } finally {
        if (!success) {
            Abort();
        }
    }
}

23voto

MichaelGG Puntos 8202

Escribí un orden superior de la función para que funcione. Hemos usado esta en varios proyectos y se parece a un gran trabajo. Así es como las cosas deberían haber hecho desde el principio, sin el "uso" paradigma o así.

Usted puede hacer llamadas como esta:

int a = 1; 
int b = 2; 
int sum = UseService((ICalculator calc) => calc.Add(a, b)); 
Console.WriteLine(sum);

Esto es más o menos igual que el de tu ejemplo. En algunos proyectos, podemos escribir el establecimiento inflexible de tipos de métodos de ayuda, así que se termina de escribir cosas como "Wcf.UseFooService(f=>f...)".

Me parece bastante elegante, considerando todas las cosas. ¿Existe un problema?

Edit: debo añadir, que esto permite que otras características ingeniosas para ser enchufado. Por ejemplo, en un sitio, el sitio autentica el servicio en nombre de la sesión del usuario. (El sitio no tiene credenciales por sí mismo.) Escribiendo nuestro propio "UseService" método auxiliar, podemos configurar el canal de la fábrica de la manera que deseamos, etc. Además no estamos obligados a usar el generado proxies -- cualquier interfaz que va a hacer.

18voto

makerofthings7 Puntos 10028

Este es Microsoft recomienda manera de manejar cliente de WCF llama:

Para más detalles, consulte: Espera Excepciones

try
{
    ...
    double result = client.Add(value1, value2);
    ...
    client.Close();
}
catch (TimeoutException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}
catch (CommunicationException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}

Información adicional Así que muchas personas parecen estar haciendo esta pregunta en WCF que Microsoft creado incluso una dedicada de ejemplo para demostrar cómo manejar excepciones:

c:\WF_WCF_Samples\WCF\Basic\Client\ExpectedExceptions\CS\client

Descargar el ejemplo: C# o VB

Teniendo en cuenta que hay muchos problemas que implican el uso de la instrucción, (climatizada?) Las discusiones internas y los hilos sobre este tema, no voy a perder mi tiempo tratando de convertirse en un código de vaquero y encontrar un modo más limpio. Sólo voy a chupar, y aplicar WCF los clientes de este detallado (todavía de confianza) de manera de que mi servidor de aplicaciones.

Opcional Errores Adicionales para la captura de

Muchas de las excepciones derivan de CommunicationException y no creo que la mayoría de esas excepciones se debe volver a intentar. Yo drudged a través de cada una excepción en MSDN y encuentra una breve lista de reintento-capaz de excepciones (además de a TimeOutException anterior). Me dejan saber si me perdí de una excepción que se debe volver a intentar.

  // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
catch (ChannelTerminatedException cte)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

// 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)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

// The following exception that is thrown when a server is too busy to accept a message.
catch (ServerTooBusyException stbe)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

Sin duda, este es un poco mundano de código a escribir. Yo actualmente prefieren esta respuesta, y no veo ninguna "hacks" en que el código que puede causar problemas en el camino.

13voto

Neil Puntos 1450

por fin he encontrado algunos sólidos pasos hacia una solución limpia para este problema.

Esta herramienta personalizada extiende WCFProxyGenerator para proporcionar un manejo de excepciones de proxy. Se genera un proxy adicionales llamados ExceptionHandlingProxy<T> de la que hereda ExceptionHandlingProxyBase<T> - el último de los cuales implementa la carne de la representación de la funcionalidad. El resultado es que usted puede elegir para utilizar el proxy predeterminado que hereda ClientBase<T> o ExceptionHandlingProxy<T> que encapsula la gestión de la vida útil de la fábrica de canales y el canal. ExceptionHandlingProxy respeta sus selecciones en Agregar Referencia de Servicio diálogo con respecto a los métodos asincrónicos y tipos de colección.

Codeplex tiene un proyecto llamado el Manejo de excepciones Generador de Proxy de WCF, que básicamente se instala una nueva herramienta personalizada para visual studio 2008, a continuación, utilizar esta herramienta para generar el nuevo servicio de proxy (Agregar referencia de servicio), que tiene algunas funcionalidades que lidiar con falla canales, tiempos de espera y eliminación segura. Hay un excelente video que aquí se ha llamado ExceptionHandlingProxyWrapper que explica exactamente cómo funciona esto.

Usted puede utilizar con seguridad la Using declaración de nuevo, y si el canal es colocada en cualquier solicitud de (TimeoutException o CommunicationException), el Contenedor se re-inicializar el fallo de canal y vuelva a intentar la consulta, y si falla, entonces se llamará al Abort() de comando y disponer de la representación y rethrow la Excepción, si el servicio se lanza FaultException código detendrá la ejecución y el proxy será anulado de forma segura a tirar la excepción correcta como se esperaba.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X