5 sept 2012

WCF 4 Parte 2: Creando el cliente

Continuando con el post anterior vamos a elaborar la aplicación cliente para probar nuestro servicio WCF. Recordar que estas son notas del libro “Windows CommunicationFoundation 4”,  preferiblemente leerlo.

Primero a nuestra solución añadimos un nuevo proyecto tipo consola (yo lo nombré como ProductoCliente), seguidamente añadimos la referencia a System.ServiceModel esto en la pestaña .Net.

Ahora necesitamos añadir la referencia a nuestro proyecto Web que aloja el servicio WCF, esto para crear un proxy por medio del cual establecer la comunicación con el servicio. Procedemos de la siguiente manera: sobre el proyecto recién creado hacemos clic derecho y elegimos “Add Service Reference…” nos aparecerá una pantalla como esta:


En esta hacemos clic sobre el botón Discover, esto realiza una búsqueda de servicios en nuestra solución. Elegimos el servicio asegurándonos que nuestro método exista en la sección “Operations” y hacemos clic en OK:


Importante notar que se cambió el namespace para hacerlo más descriptivo. Con esto hemos creado el proxy necesario para comunicarnos con el servicio, pero ¿Dónde esta este Proxy? Lo podemos encontrar haciendo clic el botón de mostrar todos los archivos y luego expandiendo los folder: Service Reference, TestWCF, Reference.svmap y haciendo doble clic sobre el archivo Refence.cs


Este proxy fue creado automáticamente por Visual Studio, por lo que no es recomendable modificarlo manualmente ya que los cambios se perderán cada vez que se actualice la referencia.

Ahora podemos dedicarnos a escribir el código de la aplicación cliente que consumirá nuestro servicio WCF. El archivo Program.cs quedaría similar a esto:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using ProductoCliente.TestWCF;

namespace ProductoCliente
{
    class Program
    {
        static void Main(string[] args)
        {
            // Se crea el proxy para conectar con el servicio
            ProductoServicioClient proxy = new ProductoServicioClient();

            // Probamos el metodo que obtiene los id de productos
            Console.WriteLine("Listado de Id de Productos");
            string[] ListaProductos = proxy.ListaProductos();
            foreach (string idProdcto in ListaProductos)
            {
                Console.WriteLine("Id Producto: {0}", idProdcto);
            }
            Console.WriteLine();

            // Desconectado del servicio
            proxy.Close();
            Console.WriteLine("Presione ENTER para terminar.");
            Console.ReadLine();

        }
    }
}

El código es fácil de entender, lo único relevante de señalar es la desconexión del proxy; es importante realizar esta tarea para liberar cualquier recurso abierto en el servidor donde esta alojado el servicio WCF. Esta tarea también se puede lograr usando la  sentencia  using (ProductoServicioClient proxy = new ProductoServicioClient())  {…} ya que la clase del servicio implementa la interfaz IDisposable, lo que significa que al cerrar el bloque using se llamará automáticamente al método close del proxy.

Si ejecutamos esta consola obtendremos los id de los productos registrados en la base de datos.


Pero aquí es donde debemos detenernos y hacernos preguntas ¿Cómo la aplicación cliente sabe donde encontrar el servicio? ¿Cómo maneja los tipos del mensaje de respuesta del servicio? Esto es importante porque estas cuestiones son la esencia detrás del WCF.

Revisemos el App.config de nuestra aplicación cliente:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IProductoServicio" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:50533/WCFServiceTest/Service.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IProductoServicio"
                contract="TestWCF.IProductoServicio" name="BasicHttpBinding_IProductoServicio" />
        </client>
    </system.serviceModel>
</configuration>

Todo esto se creó cuando añadimos la referencia al servicio. En este momento nos interesa la sección <client> es aquí donde se definen los endpoints, estos proveen los detalles por medio de los cuales se vuelve posible la comunicación con el servicio. Estos detalles son:

-        -  Address: es la ruta donde el servicio esta localizado, en este ejemplo como el servicio se encuentra hosteado en el servidor de desarrollo de Visual Studio la ruta es una local via http y con un puerto generado por el mismo servidor.

-        -  Binding y bindingConfiguration: especifica cosas como el mecanismo de trasporte, el protocolo de comunicación, entre otros. Existen bindings preestablecidos dentro del WCF, si no se especifica ninguno se utiliza el de por defecto, que es basicHTTPBinding, el cual es compatible con la mayoría de Servicios Web existentes. Ademas se pueden especificar cosas como Timeouts, encodings para los mensajes y requerimientos de seguridad. Es muy importante señalar que la aplicación cliente debe usar la misma información de binding que el servicio para que la comunicación sea exitosa. Si se cambia el binding en el servicio debe replicarse ese cambio en todas las aplicaciones cliente que lo consuman.

-        -  Contract: es el que define los mensajes de envío y recepción para el servicio, este está especificado como una interfaz en el proxy que se crea al añadir la referencia al servicio

Adicionalmente, por medio del atributo name se puede definir un nombre para el endpoint, el cual se puede usar en código para señalar que endpoint usar por medio de una sobre carga en el constructor del proxy.

Ahora y finalmente, si recordamos la implementación de nuestro método ListaProductos en el servicio, recordaremos que el mensaje de vuelta se implementó como una lista de strings; sin embargo en nuestra aplicación cliente lo estamos recibiendo como un arreglo de strings ¿A que se debe esto?

Se debe a la manera en que se trasmiten los mensajes. Recordemos que estos se trasmiten por medio de XML. Esto es, cuando definimos operaciones en el servicio lo hacemos usando tipos del .Net framework, cuando se recibe un mensaje el WCF runtime convierte el XML entrante en tipos .Net y cuando envía la respuesta convierte tipos .Net en el XML de respuesta. Algo idéntico ocurre en la aplicación cliente. La mayoría de las veces el mapeo entre XML y tipos .Net es satisfactorio. Sin embargo para las colecciones XML hay varias representaciones válidas en tipos .Net y por defecto se utilizan arreglos para mapearlas.

Esto se puede cambiar de la siguiente manera: hacemos clic derecho sobre la referencia del servicio, en el menú emergente elegimos “Configure Service Reference…” nos aparecerá una pantalla como la siguiente:




En el combo “Collection Type” elegimos System.Collections.Generic.List y hacemos clic en OK. Ya con este cambio nuestra aplicación cliente ya no se ejecuta, puesto que nos da un error de conversión de tipos entre el arreglo y la lista genérica. Hacemos un ajuste de manera que cambiamos el array por:

List<string> ListaProductos = proxy.ListaProductos();

Ejecutamos nuevamente y obtenemos el mismo resultado, sólo que ahora obtenemos una lista genérica con los id de los productos en lugar de un arreglo con los mismos.

En el siguiente post vamos a agregar más operaciones a nuestro servicio…

Roy {aka. Foy}

Autor & Editor

Desarrallador y líder técnico, con experiencia en tecnologías Microsoft desde los tiempos del VB6 y el asp clásico hasta el .Net Core, pasando por COM+, javascript, angularjs, Ionic, xaml, cordova, MVC, Web Api, Sql Server, Oracle... . Ávido lector, apasionado programador.