martes, 22 de enero de 2013

WCF ChannelFactory<T>. Evitar el uso de svcUtil para generar clientes WCF.

A la hora de crear un cliente para un servicio

WCF

podemos hacer algo tan sencillo y rápido como botón derecho "Agregar referencia de Servicio" y aquí introducir la URL que contiene los meta-datos del servicio.

Visual Studio en este punto usa svcUtil para generar automáticamente las clases proxy y los tipos que se usarán en el servicio.

Hasta aquí todos estaremos de acuerdo que no hay modo más sencillo y rápido de generar un cliente que usando svcUtil. En caso que estemos trabajando en una arquitectura en la que disponemos de los tipos y la interfaz que se ha usado para crear el servicio, crear un "service reference" sería práctico pero poco óptimo dado que estaríamos generando clases y archivos que realmente no sería necesario crear en presentación (cliente).

Hoy veremos como usar la clase

ChannelFactory<T>

que nos permitirá generar un cliente para nuestro servicio. Para el ejemplo será necesario crear 3 proyectos; AreaTIC.Client (Consola), AreaTIC.Server (Consola) y AreaTIC.Shared (librería de clases).

Crearemos una solución que contenga los proyectos AreaTIC.Server y AreaTIC.Shared. En primer lugar crearemos los tipos que usaremos en las operaciones del servicio (

DataContract

). Para implementar el patrón estos tipos necesitaremos que sean accesibles tanto en servidor como cliente así que ubicaremos las clases en el proyecto AreaTIC.Shared en un namespace AreaTIC.Shared.DataContract.
namespace AreaTIC.Shared.DataContracts
{
    [DataContract]
    public class Tipo1
    {
        [DataMember]
        public string Atributo1 { get; set; }
        [DataMember]
        public string Atributo2 { get; set; }
    }
}
En segundo lugar, para crear un servicio

WCF

nos hará falta una interfaz con el atributo

[ServiceContract]

que contenga la firma de los métodos que se incluirán en el servicio. Dicha interfaz será necesaria tanto en servidor como cliente así que la ubicaremos dentro del proyecto AreaTIC.Shared en un namespace que llamaremos AreaTIC.Shared.ServiceContracts.
namespace AreaTIC.Shared.ServiceContracts
{
    [ServiceContract]
    public interface IServiceEjemplo
    {
        [OperationContract]
        string FuncionEjemplo(Tipo1 pValue);
    }
}
Crearemos la clase que implementará la interfaz anterior dentro de AreaTIC.Server.Services.
using AreaTIC.Shared.ServiceContracts;
using AreaTIC.Shared.DataContracts;

namespace AreaTIC.Server.Services
{
    public class ServiceEjemplo:IServiceEjemplo
    {
        string IServiceEjemplo.FuncionEjemplo(Tipo1 pValue)
        {
            Thread.Sleep(5000);

            //Implementación (...)

            return "bla bla";
        }
    }
}
Para acabar con la parte servidor faltaría definir un

Binding

y hospedar el servicio (esta parte podría hacerse vía web.config

<system.servicemodel>

).
using System.ServiceModel;
using AreaTIC.Server.Services;
using AreaTIC.Shared.ServiceContracts;

namespace AreaTIC.Server
{
 class Program
 {
 static void Main(string[] args)
 {
  try
  {
    Uri baseURI = new Uri("http://localhost:8080/Services/");
    string Address = "EjemploAreaTIC";
    ServiceHost hostEjemplo = new ServiceHost(typeof(ServiceEjemplo), baseURI);
    string ContractTypeName="AreaTIC.Shared.ServiceContracts.IServiceEjemplo";

    WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);
    hostEjemplo.AddServiceEndpoint(ContractTypeName, binding, Address);

    Console.WriteLine("Servicio Ejemplo a la escucha");
  }
  catch (Exception)
  {
    Console.WriteLine("Error al hospedar el servicio");
  }
  finally 
  {
    Console.ReadLine();
  }
 }
 }
}
Llegado este punto ya tenemos el servicio a la escucha ahora veamos como generar el cliente usando

ChannelProxy

. En primer lugar crearemos otra solución que contenga un nuevo proyecto AreaTIC.Client al que añadiremos como referencia la librería de clases AreaTIC.Shared. En la misma clase Program.cs definiremos el

binding

,

endPointAddress

y conectaremos al servicio que hemos creado en el paso anterior mostrando la información que devuelve la llamada por pantalla.
using System.ServiceModel;
using AreaTIC.Shared.DataContracts;
using AreaTIC.Shared.ServiceContracts;

namespace AreaTIC.Client
{
 class Program
 {
   static void Main(string[] args)
   {
     //definimos binding.
     WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);

     //definimos endpoint 
     EndpointAddress endPoint = new EndpointAddress("http://localhost:8080/Services/EjemploAreaTIC");

     //definimos factory y creamos canal.
     ChannelFactory<IServiceEjemplo> factory = new ChannelFactory<IServiceEjemplo>(binding,endPoint);
            
     //en este punto podríamos personalizar el comportamiento de nuestro cliente (serializador, inspectors, credenciales, certificados, ...);
     
     factory.Open();
     IServiceEjemplo wcfClient = factory.CreateChannel();
     Console.WriteLine(wcfClient.FuncionEjemplo(new Tipo1()));
     Console.ReadLine();
     factory.Close();
    }
 }
}
Si todo ha ido bien, mostraremos el texto 'bla bla' por pantalla. En arquitecturas donde se accede a datos vía

WCF

es muy interesante usar

ChannelFactory<T>

en cliente para evitar tener una lista interminable de "Service Reference" que serán engorrosos de configurar vía app.config.

Hasta aquí el artículo de hoy. Espero les sea de ayuda y como siempre os animo a participar con vuestros comentarios y/o aportaciones. Recuerda que puedes seguir

areaTIC

en las redes sociales!


2 comentarios:

Manu dijo...

Hola Carlos,

Soy Manu Delgado, del educem ;).. Muy buena entrada, pero esto nos soluciona el tema siempre y cuando podamos tener acceso a la propia librería del servicio no??? Es decir si el servicio se va a consumir desde el exterior u otro servicio externo se tendrá que obtener el schema de los metadatos imagino no ???

Carlos Cañizares dijo...

Buenas!!

Exacto como bien dices si no disponemos de la interfaz y tipos en un a dll compartida de algún modo tendríamos que obtenerla de los metadatos y entiendo que podríamos usar los archivos descargados igualmente con channelfactory para crear un canal... pero no sé hasta que punto ya sería práctico. Lo veo muy útil siempre que compartas entre cliente y servidor los tipos y la interfaz.

Recuerdos Manu!

Publicar un comentario en la entrada