¿Cuál es la mejor solución para el problema de bloqueo del cliente WCF `using`?

Me gusta crear instancias de mis clientes de servicio WCF dentro de un using block, ya que es prácticamente la forma estándar de usar recursos que implementan IDisposable:

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

Pero, como se indica en este artículo de MSDN, envolviendo un cliente WCF en un using El bloque podría enmascarar cualquier error que provoque que el cliente quede en un estado de falla (como un tiempo de espera o un problema de comunicación). En pocas palabras, cuando Dispose() se llama, el cliente Close() El método se activa, pero arroja un error porque está en un estado defectuoso. La excepción original queda enmascarada por la segunda excepción. No está bien.

La solución alternativa sugerida en el artículo de MSDN es evitar por completo el uso de using bloquear y, en su lugar, crear una instancia de sus clientes y usarlos de esta manera:

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 mucho código para escribir cada vez que necesite un cliente.

Afortunadamente, encontré algunas otras soluciones, como esta en el blog (ahora desaparecido) IServiceOriented. Empiece 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(); 
            } 
        } 
     } 
} 

Que luego permite:

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

Eso no está mal, pero no creo que sea tan expresivo y fácilmente comprensible como el using bloquear.

La solución que estoy tratando de usar actualmente, la primera vez que leí en blog.davidbarret.net. Básicamente, anula la del cliente Dispose() método donde sea que lo use. Algo como:

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

Esto parece ser capaz de permitir using bloquear de nuevo sin el peligro de enmascarar una excepción de estado defectuoso.

Entonces, ¿hay otras trampas que deba tener en cuenta para usar estas soluciones? ¿A alguien se le ha ocurrido algo mejor?

preguntado el 21 de febrero de 09 a las 21:02

El último (que inspecciona this.State) es una carrera; es posible que no tenga fallas cuando verifique el booleano, pero podría tener fallas cuando llame a Close (). -

Lees estado; no tiene fallas. Antes de llamar a Close (), el canal falla. Lanzamientos cercanos (). Juego terminado. -

El tiempo pasa. Puede ser un período de tiempo muy corto, pero técnicamente, en el período de tiempo entre la verificación del estado del canal y la solicitud de cierre, el estado del canal puede cambiar. -

Yo usaría Action<T> en lugar de UseServiceDelegate<T>. menor. -

Realmente no me gusta este ayudante estático Service<T> ya que complica las pruebas unitarias (como hacen la mayoría de las cosas estáticas). Preferiría que no sea estático para que pueda inyectarse en la clase que lo está usando. -

0 Respuestas

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.