Manejando excepciones en WCF

En WCF las excepciones se trasmiten desde el servicio hacia el cliente (ó en un escenario duplex desde el cliente hacia el servicio) mediante objetos SOAP faults.  Sin embargo para lograr esta funcionalidad es necesario especificar manualmente las excepciones que cada operación en el servicio puede manejar, a continuación se ejemplariza estas definiciones con dos servicios en WCF; el primero en un escenario one-way y el segundo en un escenario duplex.

Escenario one-way 

En el siguiente escenario se crea una interfaz llamada IBlogs la cual define el metodo CrearPost(string post) , el cual implementa la lógica del servicio en la clase BlogsService.  Así mismo, se especifica el atributo FaultContractAttribute al método CrearPost() soportando de esta forma excepciones representadas por objetos BlogsFault. El objeto BlogsFault define una variable llamada

 [ServiceContract(Namespace="https://wcf.biztalklatam.com/excepciones")]
interface IBlogs
{
    [OperationContract]
    [FaultContract(typeof(BlogsFault))]
    void CrearPost(string post);
}

[DataContract(Namespace="https://schemas.biztalklatam.com/excepciones")]
class BlogsFault
{
    [DataMember]
    public string DetalleError;
    
    public BlogsFault(string error)
    {
        DetalleError = error;
    }
}

class BlogsService : IBlogs
{
    public void CrearPost(string post)
    {            
        if (post == null || post.Length == 0)
        {
            Console.WriteLine("Error al leer el mensaje.!!");
            throw new FaultException<BlogsFault>(new BlogsFault("El mensaje es incorrecto"));
        }
        Console.WriteLine(string.Format("Se escribe el post: {0}", post));
    }
}

La implementación del cliente invoca al servicio dos veces, inicialmente para publicar un post con contenido el cual no genera una excepción.  La segunda ocasión intenta publicar un post sin contenido el cual genera una excepción, en este caso la excepcion se captura utilizando la clase FaultException<> y especificando el tipo de objeto del error (BlogsFault para esta ocasión).

 class Program
{
    static void Main(string[] args)
    {
        BlogsClient cliente = new BlogsClient();
        // se crea un post con contenido, no debe generar error 
        cliente.CrearPost("Primer post");

        // se crea un post vacio, debe generar error
        try
        {
            cliente.CrearPost("");
        }
        catch (FaultException<BlogsFault> e)
        {
            Console.WriteLine(string.Format("Error: {0}", e.Detail.DetalleError));
        }
    }
}

Escenario duplex

En esta ocasión el servicio solicita una confirmación al cliente antes de publicar el post.  En este caso se implementa una nueva interfaz llamada IBlogsCallback que define el método ConfirmarPost() , a su vez la interfaz IBlogs que define el servicio debe establecer IBlogsCallback como el objeto CallbackContract .

El método ConfirmarPost() será invocado desde el servicio, por lo que su lógica deberá ser implementada en el cliente.  Y en adición, se debe especificar el atributo FaultContractAttribute  para que este método soporte excepciones con objetos tipo BlogsFault.

De igual forma es necesario establecer que el servicio acepte operaciones multi-threaded.  Para esto se debe especificar el tipo de concurrencia como  ConcurrencyMode.Reentrant  ó ConcurrencyMode.Multiple  en el atributo ServiceBehaviorAttribute a la clase que implementa el servicio, en este caso BlogsServicio

 [ServiceContract(Namespace="https://wcf.biztalklatam.com/excepciones",
CallbackContract=typeof(IBlogsCallback))]
interface IBlogs
{
    [OperationContract]
    [FaultContract(typeof(BlogsFault))]
    void CrearPost(string post);
}

interface IBlogsCallback
{
    [OperationContract]
    [FaultContract(typeof(BlogsFault))]
    void ConfirmarPost(string post);
}

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
class BlogsService : IBlogs
{
    public void CrearPost(string post)
    {        
        if (post == null || post.Length == 0)
        {
            Console.WriteLine("Error al leer el mensaje.!!");
            throw new FaultException<BlogsFault>(new BlogsFault("El mensaje es nulo o vacio"));
        }

        try
        {
            Callback.ConfirmarPost(post);
        }
        catch(FaultException<BlogsFault> e)
        {
            Console.WriteLine(string.Format("Post no confirmado: {0}", e.Detail.DetalleError));
            return;
        }

        Console.WriteLine(string.Format("Se escribe el post: {0}", post));
    }

    public IBlogsCallback Callback
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel<IBlogsCallback>();
        }
    }
}

[DataContract(Namespace="https://schemas.biztalklatam.com/excepciones")]
class BlogsFault
{
    [DataMember]
    public string DetalleError;

    public BlogsFault(string error)
    {
        DetalleError = error;
    }
}

Por último, el cliente tambien debe ser modificado para aceptar la llamada de confirmación desde el servicio.  En esta ocasión se implementa una clase que derive de la interfaz IBlogsCallback y que implemente el metodo ConfirmarPost() el cual es invocado desde el servicio, en caso de no haber confirmación por parte del usuario el método genera una excepcion que será transmitida hacia el servicio como un objeto SOAP fault. 

 public class Blogs : IBlogsCallback
{
    public void ConfirmarPost(string post)
    {
        Console.WriteLine(string.Format("Desea escribir el post: \"{0}\" ? Y/N", post));
        string result = Console.ReadLine();

        if (result == "N")
        {
            BlogsFault fault = new BlogsFault();
            fault.DetalleError = "Post Malo.!";
            throw new FaultException<BlogsFault>(fault);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Blogs confirmar = new Blogs();
        InstanceContext ctx = new InstanceContext(confirmar);
        BlogsClient cliente = new BlogsClient(ctx);
        // se crea un post con contenido, no genera error y se solicita confirmacion al cliente
        cliente.CrearPost("Primer post");

        // se crea un post vacio, debe generar error y no solicita confirmacion al cliente
        try
        {
            cliente.CrearPost("");
        }
        catch (FaultException<BlogsFault> e)
        {
            Console.WriteLine(string.Format("Error: {0}", e.Detail.DetalleError));
        }
    }
}

 

Autor: Carlos Medina
Este mensaje se proporciona "como está" sin garantías de ninguna clase, y no otorga ningun derecho