Una Mojca Más

Un teleco infiltrado en el lado oscuro

Tutorial WCF (2 de 5)

Articulos anteriores:
Tutorial WCF (1 de 5)

2 - MONTANDO UN SERVICIO

La idea principal es que nuestro servicio será independiente de la forma en que nos comunicaremos con él.

El proceso de creación de un servicio se divide en dos partes:

  1. Definición y creación de un contrato: en éste definiremos la funcionalidad (métodos accesibles) que tendrá nuestro servicio y que datos (clases, estructuras, ...) utilizará para comunicarse.
  2. Creación de un servicio que "implemente" el contrato definido.

Así que esto es lo que haremos en este primer tutorial.

DEFINIENDO EL CONTRATO

Un contrato no es más que una interfaz tal y como la conocemos en el concepto de la POO. Vamos a definirlo.

Sin usar la plantilla que nos ofrecen las extensiones o "Orcas" por defecto para crear una WCF service Library, creamos un proyecto que sea de tipo librería en el lenguaje que más os guste; yo lo haré en C#. Para empezar crearemos una nueva interfaz en nuestro proyecto y la llamaremos IMiServicio. Es importante saber que para trabajar con WCF necesitamos añadir una referencia a nuestro proyecto (si no está ya) y hacer el "import/using" pertinente: System.ServiceModel.

   1: using System;
   2: using System.Runtime.Serialization;
   3: using System.ServiceModel;
   4: using System.Collections.Generic;
   5:  
   6: namespace BlogWCF
   7: {
   8:     [ServiceContract]
   9:     public interface IMiServicio
  10:     {
  11:  
  12:         [OperationContract]
  13:         List<Cumple> DameCumples();
  14:  
  15:         [OperationContract]
  16:         int DimeEdad(Cumple cu);
  17:     }
  18:  
  19:     [DataContract]
  20:     public class Cumple
  21:     {
  22:         string nombre = "";
  23:         DateTime fechaCumple = new DateTime();
  24:  
  25:         [DataMember]
  26:         public string Nombre
  27:         {
  28:             get { return nombre; }
  29:             set { nombre = value; }
  30:         }
  31:  
  32:         [DataMember]
  33:         public DateTime FechaCumple
  34:         {
  35:             get { return fechaCumple; }
  36:             set { fechaCumple = value; }
  37:         }
  38:     }
  39: }

Para indicar que se trata de un contrato usamos el atributo ServiceContract al declarar la Interface. Para definir qué métodos tendrá el contrato usamos el atributo OperationContract.
Este contrato es el que usarán los clientes para saber que operaciones tiene un servicio y que datos intervienen en la comunicación.
En este caso usamos una clase nuestra llamada Cumple que interviene en la comunicación y deberá ser serializada. Para indicar que la clase también debe estar en el contrato usamos los atributos DataContract y DataMember para la deficinión de la clase y sus propiedades respectivamente. Esta clase debe cumplir los requisitos para que sea serializable ya que debe poder ser enviada mediante SOAP y XML si lo deseamos.

CREANDO EL SERVICIO

Ahora lo que debemos hacer es implementar esta Interfaz (contrato), y crear la clase que dará este servicio:

   1: using System;
   2: using System.Collections.Generic;
   3:  
   4: namespace BlogWCF
   5: {
   6:     public class MiServicio : IMiServicio
   7:     {
   8:         public List<Cumple> DameCumples()
   9:         {
  10:             List<Cumple> cumples = new List<Cumple>();
  11:             cumples.Add(new Cumple{
  12:                             Nombre="Jordi",
  13:                             FechaCumple=new DateTime(1982,10,8)});
  14:             cumples.Add(new Cumple {
  15:                             Nombre = "Alex",
  16:                             FechaCumple = new DateTime(1982, 6, 13) });
  17:             cumples.Add(new Cumple {
  18:                             Nombre = "Clara",
  19:                             FechaCumple = new DateTime(1982, 3, 20) });
  20:  
  21:             return cumples;
  22:         }
  23:  
  24:         public int DimeEdad(Cumple cu)
  25:         {
  26:             int years = DateTime.Now.Year - cu.FechaCumple.Year -
  27:                 (cu.FechaCumple.Month > DateTime.Now.Month ? 1 :
  28:                 (cu.FechaCumple.Month != DateTime.Now.Month) ? 0 :
  29:                 (cu.FechaCumple.Day > DateTime.Now.Day) ? 1 : 0);
  30:             return years;
  31:         }
  32:     }
  33: }

Vemos que hemos creado una clase que implementa la interfaz que define el contrato. Podríamos añadir más métodos, propiedades y demás, pero no serían visibles desde un cliente ya que no están definidos en el contrato.

Ahora ya tenemos definido el servicio y lo que tenemos de decidir es cómo se va a acceder a él; SOAP y XML (Web Services), mediante HTTP/HTTPS, TCP,... y quien lo va a servir; el IIS, un servicio del sistema operativo, lo serivá otro programa, etc.
Pero esto se hace a parte de la definición del servicio, así separamos lo que es la funcionalidad de las comunicaciones.
Además, la definición de las comunicaciones la haremos mediante ficheros de configuración basados en XML, o sea, legibles y fácilmente editables. Así que, si decidimos un día cambiar un servicio que se está sirviendo como un servicio web a un servicio que se sirva por TCP por el puerto 8080, solo habrá que cambiar dos lineas del fichero de configuración y listos; no habrá que modificar el código de nuestro servicio.

Esto lo veremos en el próximo post: Sirviendo un Servicio de WCF.

Posted: 10/8/2007 10:29 por napilut | con 11 comment(s) |
Archivado en:
Comparte este post:

Comentarios

Juan Gomez ha opinado:

Una pregunta: ¿Porqué estas definiendo los campos privados dentro del DataContract?

# August 10, 2007 12:47 PM

napilut ha opinado:

Al fin y al cabo esto es solo un ejemplo ilustrativo y educativo, pero tampoco está mal hecho bajo mi parecer.

El contrato no deja de ser una definición de una interface. En este caso, para poder utilizar el servicio, necesito de una clase auxiliar: la clase Cumple. Quiero que el cliente sea capaz de instanciarla y recivirla/enviarla, así que debo ponerla en el contrato, indicando los campos quiero que el usuario vea y no vea.

En este caso he creado unas variables privadas en mi clase a las que se accede a través de sus propiedades. El cliente solo tiene que saber que la clase Cumple tiene dos propiedades públicas a las que puede acceder y editar y por eso les pongo el atributo [DataMember].

Al final solo estoy definiendo una clase como siempre lo hemos hecho, pero después indicamos qué campos de esta clase van a ser serializados en el contrato, o sea, accesibles para el cliente.

No se si me he explicado. :P

# August 10, 2007 1:12 PM

Anonimo ha opinado:

También me interesa saber el motivo. Yo creo que estas exponiendo un contrato de datos. Esa clase no se va a utilizar desde el cliente a menos que tenga el atributo de DataMember en alguna propiedad. A menos que por algún motivo quisieras utilizar las variables privadas desde el servidor, no tiene sentido definirlos. Y no veo el uso que les podrías dar.  

# August 10, 2007 2:54 PM

napilut ha opinado:

Tiene el atributo DataMember en las dos propiedades que utiliza el cliente.

Al ser propiedades, éstas deben editar alguna variable: en este caso las privadas. Yo no puedo definir dos propiedades sin que estas editen alguna variable.

Podría haver definido directamente la clase Cumple así, sin utilizar propiedades:

[DataContract]

public class Cumple

{

  [DataMember]

  public string Nombre;

  [DataMember]

  public DateTime Fecha;

}

O utilizar la nueva forma de definirlas (supongo):

[DataContract]

public class Cumple

{

  [DataMember]

  public string Nombre {get;set;}

  [DataMember]

  public DateTime Fecha {get;set;}

}

Pero he utilizado la forma básica de una propiedad:

-atributo privado:

private string nombre;

-propiedad public que accede al atributo privado:

public string Nombre

{

    get { return nombre; }

    set { nombre = value; }

}

Por eso hay los dos atributos.

Además, si creas tu un proyecto de "WCF Service Library" con Visual Studio Orcas. La misma Template te crea una clase que está marcada como DataContract que tiene propiedades como la mia, con su atributo privado:

Ejemplo de DataContract que da VS Orcas Beta 1:

[DataContract]

public class CompositeType

{

  bool boolValue = true;

  string stringValue = "Hello ";

  [DataMember]

  public bool BoolValue

  {

      get { return boolValue; }

      set { boolValue = value; }

  }

  [DataMember]

  public string StringValue

  {

      get { return stringValue; }

      set { stringValue = value; }

  }

}

# August 10, 2007 4:14 PM

napilut ha opinado:

Si he hecho algo mal y creéis que debo corregirlo decírmelo: ni soy un genio, ni perfecto y, como dije en mi primer post, estoy a quí para aprender e intentar compartir. Sobretodo para aprender.

Me falta mucho para llegar al nivel de muchos de los genios que hay aquí, pero con vuestra ayuda no será difícil.

gracias.

# August 10, 2007 4:17 PM

Anonimo ha opinado:

Yo no estoy criticando tu post, ni mucho menos :). Me interesaba a ver si había algún motivo detrás de hacer eso. Sigo creyendo sin embargo que si los campos no son necesarios, no hay que declararlos. Con las propiedades es suficiente.

# August 10, 2007 6:10 PM

napilut ha opinado:

No nos hemos entendido. :P

Ya se que no estas criticando mi post, en ningún momento he sentido eso. Lo que veo es que no nos hacemos entender.

Veamos si esta vez...

Si yo no declaro los atributos privados, ¿a qué variable va a acceder la propiedad?; al fin y al cabo las propiedades son, en este caso, encapsulamientos del acceso a unas variables que me guardarán la información del nombre y la fecha. Si yo no declaro esas variables... ¿donde se guardan los datos?, en todo caso hago directamente (como en el ejemplo de antes) los atributos públicos y elimino las propiedades.

Nos entendemos ahora?

# August 11, 2007 12:22 AM

Anonimo ha opinado:

Nos entendemos perfectamente, pero la cuestión no es donde se guarda. El valor se guardará en la propiedad publica. El tener un campo privado es para encapsular la implementación y/o definición de una propiedad publica. El contrato de datos es un interfaz de tu servicio al publico y como tal sirve para el intercambio de información. Ni el servidor ni el cliente deben realmente utilizar para otro fin.

# August 11, 2007 1:03 AM

napilut ha opinado:

Tienes toda la razón.

Como ya he dicho en una de las respuestas yo haría las atributos públicos sin encapsularlos en propiedades, ya que en esta clase de intercambio, como bién dices, no tiene motivo.

Pero como en los primeros ejemplos de cualquier libro o en el ejemplo que te da la plantilla de Visual Studio se ponen las propiedades, he pensado que en mi ejemplo, al ser también un ejemplo fácil y didáctico, también debería ponerlas.

Pero creo que, como tienes toda la razón, mejor que ponga el ejemlo de la forma que en realidad lo hago.

Gracias por tu aportación.

Ahora mi duda es... ¿cambio el post?, si lo cambio toda esta serie de comentarios no vendrán al caso... :P

# August 11, 2007 12:50 PM

Sergio Tarrillo ha opinado:

Hola Jordi!

Puedes hacer un comentario en otro post, para dejar este como original :).

Saludos,

# August 12, 2007 5:09 AM

napilut ha opinado:

Una cosa sí que me gustaría aclarar.

Según las buenas bases de programación y demás que a mi me han enseñado (vaya, que he leído); si las clases exponen atributos al exterior (atributos públicos), éstos es mejor que sean privados y vayan encapsulados en propiedades públicas.

Y, aunque la clase Cumple en este caso forma parte de un contrato, es un DataContract, sigue siendo una clase, y, siguiendo estas "buenas prácticas", sería bueno dejar los atributos tal y como están.

Para corroborar esto se ha creado la nueva forma de declarar atributos en C#, en la version 3.5 del Framework:

public string MiPropiedad {get;set;}

Que te genera, internamente, la encapsulación.

La idea está en no exponer nunca atributos, si no propiedades.

# August 17, 2007 3:47 PM