Me gusta crear instancias de mis clientes de servicio WCF dentro de un using
bloque, ya que es más o menos la forma estándar de usar recursos que implementan IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Pero, como se señaló en este artículo de MSDN , envolver un cliente WCF en un using
bloque podría enmascarar cualquier error que provoque que el cliente quede en un estado defectuoso (como un tiempo de espera o un problema de comunicación). En pocas palabras, cuando se llama a Dispose (), el método Close () del cliente se dispara, 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 sugerida en el artículo de MSDN es evitar por completo el uso de un using
bloque y, en su lugar, crear una instancia de sus clientes y usarlos de la siguiente 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 es feo. Y mucho código para escribir cada vez que necesite un cliente.
Afortunadamente, encontré algunas otras soluciones, como esta en IServiceOriented. Empiezas 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 luego permite:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
Eso no está mal, pero no creo que sea tan expresivo y fácil de entender como el using
bloque.
La solución que estoy tratando de usar actualmente la leí por primera vez en blog.davidbarret.net . Básicamente, usted anula el Dispose()
método del cliente 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 el using
bloqueo nuevamente sin el peligro de enmascarar una excepción de estado con falla.
Entonces, ¿hay otras trampas que deba tener en cuenta al usar estas soluciones? ¿Alguien ha encontrado algo mejor?
Action<T>
lugar deUseServiceDelegate<T>
. menor.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.Respuestas:
En realidad, aunque escribí en un blog (ver la respuesta de Luke ), creo que esto es mejor que mi envoltorio desechable ID. Código típico:
(editar por comentarios)
Dado que
Use
devuelve vacío, la forma más fácil de manejar los valores de retorno es a través de una variable capturada:fuente
public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
yhttps://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/
ehttp://dzimchuk.net/post/wcf-error-helpers
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
Se puede elegir entre la solución recomendada por IServiceOriented.com y la solución recomendada por el blog de David Barret , prefiero la simplicidad ofrecida al anular el método Dispose () del cliente. Esto me permite seguir usando la instrucción using () como cabría esperar con un objeto desechable. Sin embargo, como señaló @Brian, esta solución contiene una condición de carrera en la que el Estado podría no tener una falla cuando se verifica, pero podría ser para cuando se llame a Close (), en cuyo caso todavía se produce CommunicationException.
Entonces, para evitar esto, he empleado una solución que combina lo mejor de ambos mundos.
fuente
success
bandera? ¿Por qué notry { Close(); } catch { Abort(); throw; }
?Close(); success = true;
? No quisiera que se lanzara una excepción si pudiera abortarla con éxito en el bloque finalmente. Solo quisiera que se lanzara una excepción si Abort () fallara en ese caso. De esta forma, el try / catch ocultaría la posible excepción de condición de carrera y aún le permitiría abortar () la conexión en el bloque finalmente.Escribí una función de orden superior para que funcione correctamente. Lo hemos usado en varios proyectos y parece funcionar muy bien. Así es como deberían haberse hecho las cosas desde el principio, sin el paradigma de "uso", etc.
Puedes hacer llamadas como esta:
Esto es muy parecido a lo que tienes en tu ejemplo. En algunos proyectos, escribimos métodos auxiliares fuertemente tipados, por lo que terminamos escribiendo cosas como "Wcf.UseFooService (f => f ...)".
Me parece bastante elegante, considerando todas las cosas. ¿Hay algún problema en particular que encontraste?
Esto permite que se conecten otras funciones ingeniosas. Por ejemplo, en un sitio, el sitio se autentica en el servicio en nombre del usuario conectado. (El sitio no tiene credenciales por sí mismo). Al escribir nuestro propio ayudante de método "UseService", podemos configurar la fábrica de canales de la manera que queramos, etc. Tampoco estamos obligados a usar los proxies generados; cualquier interfaz servirá .
fuente
GetCachedFactory
método?Esta es la forma recomendada por Microsoft para manejar las llamadas de clientes WCF:
Para más detalles ver: Excepciones esperadas
Información adicional Muchas personas parecen estar haciendo esta pregunta en WCF que Microsoft incluso creó una muestra dedicada para demostrar cómo manejar las excepciones:
c: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ client
Descargue la muestra: C # o VB
Teniendo en cuenta que hay tantos problemas relacionados con el uso de la declaración , (¿acalorado?) Debates internos e hilos sobre este tema, no voy a perder el tiempo tratando de convertirme en un vaquero del código y encontrar una forma más limpia. Simplemente lo absorberé e implementaré clientes WCF de esta manera detallada (pero confiable) para mis aplicaciones de servidor.
Fallas adicionales opcionales para atrapar
Muchas excepciones se derivan
CommunicationException
y no creo que la mayoría de esas excepciones deba ser reintentada. Revisé cada excepción en MSDN y encontré una breve lista de excepciones reintentables (además de lasTimeOutException
anteriores). Avíseme si me perdí una excepción que debería ser reintentada.Es cierto que este es un código mundano para escribir. Actualmente prefiero esta respuesta , y no veo ningún "pirateo" en ese código que pueda causar problemas en el futuro.
fuente
"Hope this code wasn't important, because it might not happen."
aún se ejecuta ...Finalmente he encontrado algunos pasos sólidos hacia una solución limpia a este problema.
Codeplex tiene un proyecto llamado Exception Handling WCF Proxy Generator . Básicamente, instala una nueva herramienta personalizada en Visual Studio 2008, luego usa esta herramienta para generar el nuevo proxy de servicio (Agregar referencia de servicio) . Tiene una buena funcionalidad para lidiar con canales defectuosos, tiempos de espera y eliminación segura. Aquí hay un excelente video llamado ExceptionHandlingProxyWrapper que explica exactamente cómo funciona esto.
Puede
Using
volver a usar la declaración de forma segura y, si el canal presenta un error en cualquier solicitud (TimeoutException o CommunicationException), el Contenedor reiniciará el canal con error y volverá a intentar la consulta. Si eso falla, llamará alAbort()
comando y eliminará el proxy y volverá a lanzar la excepción. Si el servicio arroja unFaultException
código, dejará de ejecutarse, y el proxy se cancelará de forma segura arrojando la excepción correcta como se esperaba.fuente
En base a las respuestas de Marc Gravell, MichaelGG y Matt Davis, a nuestros desarrolladores se les ocurrió lo siguiente:
Ejemplo de uso:
Está lo más cerca posible de la sintaxis de "uso", no tiene que devolver un valor ficticio cuando se llama a un método nulo, y puede hacer varias llamadas al servicio (y devolver múltiples valores) sin tener que usar tuplas.
Además, puede usar esto con
ClientBase<T>
descendientes en lugar de ChannelFactory si lo desea.El método de extensión se expone si un desarrollador desea deshacerse manualmente de un proxy / canal.
fuente
DisposeSafely
privado es ciertamente una opción, y evitaría confusión. Puede haber casos de uso en los que alguien quiera llamarlo directamente, pero no se me ocurre ninguno.https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
@Marc Gravell
¿No estaría bien usar esto?
O lo mismo
(Func<T, TResult>)
en caso deService<IOrderService>.Use
Esto facilitaría la devolución de variables.
fuente
¿Que es esto?
Esta es la versión CW de la respuesta aceptada pero con (lo que considero completo) manejo de excepciones incluido.
La respuesta aceptada hace referencia a este sitio web que ya no existe . Para ahorrarle problemas, incluyo las partes más relevantes aquí. Además, lo modifiqué ligeramente para incluir el manejo de reintentos de excepción para manejar esos molestos tiempos de espera de red.
Uso simple del cliente WCF
Una vez que genera su proxy del lado del cliente, esto es todo lo que necesita para implementarlo.
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.
PD: He hecho de esta publicación una wiki comunitaria. No recogeré "puntos" de esta respuesta, pero prefiero que lo voten si está de acuerdo con la implementación, o lo edite para mejorarlo.
fuente
success == false
la declaración if finalA continuación se muestra una versión mejorada de la fuente de la pregunta y se extendió para almacenar en caché múltiples fábricas de canales e intentar buscar el punto final en el archivo de configuración por nombre de contrato.
Utiliza .NET 4 (específicamente: contravarianza, LINQ,
var
):fuente
UseServiceDelegate<T>
lugar deAction<T>
?Action<T>
funciona igual de bien.Un envoltorio como este funcionaría:
Eso debería permitirle escribir código como:
El contenedor podría, por supuesto, capturar más excepciones si fuera necesario, pero el principio sigue siendo el mismo.
fuente
Dispose
a un IChannel, podría arrojar una excepción si el canal está en estado defectuoso, esto es un problema ya que Microsoft especifica queDispose
nunca se debe lanzar. Entonces, lo que hace el código anterior es manejar el caso cuandoClose
arroja una excepción. SiAbort
tira, podría ser algo muy mal. Escribí una publicación de blog al respecto en diciembre pasado: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapperUtilicé el proxy dinámico de Castle para resolver el problema Dispose (), y también implementé la actualización automática del canal cuando está en un estado inutilizable. Para usar esto, debe crear una nueva interfaz que herede su contrato de servicio e IDisposable. El proxy dinámico implementa esta interfaz y envuelve un canal WCF:
Me gusta esto ya que puede inyectar servicios WCF sin que los consumidores tengan que preocuparse por los detalles de WCF. Y no hay nada adicional como las otras soluciones.
Eche un vistazo al código, en realidad es bastante simple: WCF Dynamic Proxy
fuente
Use un método de extensión:
fuente
Si no necesita IoC o está utilizando un cliente autogenerado (Referencia de servicio), puede simplemente usar un contenedor para administrar el cierre y dejar que el GC tome la base de clientes cuando esté en un estado seguro que no arroje ninguna excepción. El GC llamará a Dispose en serviceclient, y esto llamará
Close
. Como ya está cerrado, no puede causar ningún daño. Estoy usando esto sin problemas en el código de producción.Luego, cuando accede al servidor, crea el cliente y lo usa
using
en la autodisconección:fuente
Resumen
Usando las técnicas descritas en esta respuesta, uno puede consumir un servicio WCF en un bloque de uso con la siguiente sintaxis:
Por supuesto, puede adaptar esto aún más para lograr un modelo de programación más conciso específico para su situación, pero el punto es que podemos crear una implementación de
IMyService
reprenting del canal que implemente correctamente el patrón desechable.Detalles
Todas las respuestas dadas hasta ahora abordan el problema de evitar el "error" en la implementación de WCF Channel de
IDisposable
. La respuesta que parece ofrecer el modelo de programación más conciso (que le permite usar elusing
bloque para disponer de recursos no administrados) es esta , donde el proxy se modifica para implementarloIDisposable
con una implementación libre de errores. El problema con este enfoque es la capacidad de mantenimiento: tenemos que volver a implementar esta funcionalidad para cada proxy que usemos. En una variación de esta respuesta, veremos cómo podemos usar la composición en lugar de la herencia para hacer que esta técnica sea genérica.Primer intento
Parecen varias implementaciones para la
IDisposable
implementación, pero en aras de un argumento usaremos una adaptación de la utilizada por la respuesta actualmente aceptada .Armados con las clases anteriores, ahora podemos escribir
Esto nos permite consumir nuestro servicio usando el
using
bloque:Haciendo esto genérico
Todo lo que hemos hecho hasta ahora es reformular la solución de Tomás . Lo que evita que este código sea genérico es el hecho de que la
ProxyWrapper
clase debe volver a implementarse para cada contrato de servicio que queramos. Ahora veremos una clase que nos permite crear este tipo dinámicamente usando IL:Con nuestra nueva clase auxiliar ahora podemos escribir
Tenga en cuenta que también podría usar la misma técnica (con ligeras modificaciones) para clientes generados automáticamente para heredar
ClientBase<>
(en lugar de usarChannelFactory<>
), o si desea usar una implementación diferente deIDisposable
para cerrar su canal.fuente
Me gusta esta forma de cerrar la conexión:
fuente
He escrito una clase base simple que maneja esto. Está disponible como un paquete NuGet y es bastante fácil de usar.
fuente
Por lo tanto, permite escribir declaraciones de devolución muy bien:
fuente
Me gustaría agregar la implementación del Servicio de la respuesta de Marc Gravell para el caso de usar ServiceClient en lugar de ChannelFactory.
fuente
Para aquellos interesados, aquí hay una traducción VB.NET de la respuesta aceptada (a continuación). Lo he refinado un poco por brevedad, combinando algunos de los consejos de otros en este hilo.
Admito que está fuera de tema para las etiquetas de origen (C #), pero como no pude encontrar una versión VB.NET de esta excelente solución, supongo que otros también buscarán. La traducción de Lambda puede ser un poco complicada, por lo que me gustaría salvar a alguien del problema.
Tenga en cuenta que esta implementación particular proporciona la capacidad de configurar el
ServiceEndpoint
tiempo de ejecución.Código:
Uso:
fuente
La arquitectura de nuestro sistema a menudo usa el marco de trabajo de Unity IoC para crear instancias de ClientBase, por lo que no hay una forma segura de exigir que los otros desarrolladores incluso usen
using{}
bloques. Para que sea lo más infalible posible, hice esta clase personalizada que extiende ClientBase y maneja el cierre del canal al desechar o al finalizar en caso de que alguien no elimine explícitamente la instancia creada por Unity.También hay cosas que deben hacerse en el constructor para configurar el canal para credenciales personalizadas y otras cosas, así que eso también está aquí ...
Entonces un cliente puede simplemente:
Y la persona que llama puede hacer cualquiera de estos:
fuente
Remití pocas respuestas en esta publicación y la personalicé según mis necesidades.
Quería la capacidad de hacer algo con el cliente WCF antes de usarlo, así que el
DoSomethingWithClient()
método.Aquí está la clase auxiliar:
Y puedo usarlo como:
fuente
Tengo mi propio contenedor para un canal que implementa Dispose de la siguiente manera:
Esto parece funcionar bien y permite utilizar un bloque de uso.
fuente
El siguiente ayudante permite llamar
void
y métodos no anulados. Uso:La clase en sí es:
fuente
Anule Dispose () del cliente sin la necesidad de generar una clase proxy basada en ClientBase, ¡también sin la necesidad de administrar la creación y el almacenamiento en caché del canal ! (Tenga en cuenta que WcfClient no es una clase ABSTRACT y se basa en ClientBase)
fuente
Mi método para hacer esto ha sido crear una clase heredada que implemente explícitamente IDisposable. Esto es útil para las personas que usan la interfaz gráfica de usuario para agregar la referencia de servicio (Agregar referencia de servicio). Solo dejo caer esta clase en el proyecto haciendo referencia al servicio y la uso en lugar del cliente predeterminado:
Nota: Esta es solo una implementación simple de dispose, puede implementar una lógica de eliminación más compleja si lo desea.
Luego puede reemplazar todas sus llamadas realizadas con el cliente de servicio regular por los clientes seguros, de esta manera:
Me gusta esta solución, ya que no requiere que tenga acceso a las definiciones de interfaz y puedo usar el
using
declaración como esperaría, mientras que permite que mi código se vea más o menos igual.Aún tendrá que manejar las excepciones que se pueden lanzar como se señala en otros comentarios en este hilo.
fuente
También podría usar a
DynamicProxy
para extender elDispose()
método. De esta manera podrías hacer algo como:fuente