Manteniendo el estado con WCF (2 de 2)

En el post anterior hemos visto como mantener el estado y controlar las instancias que se crean de nuestro servicio.

Ahora vamos a ver como definir el orden en el que se pueden ejecutar los métodos de nuestro servicio. La idea es que el cliente no debe poder hacer un Checkout sin antes haber agregado algún producto a su carrito.

Hay una serie de propiedades «booleanas» del atributo OperationContract con nos permiten definir justamente esto:

IsInitiating
Si esta variable vale true el cliente puede usar este método como el primero, el que creará el servicio. Por defecto vale true, si la ponemos a false el cliente no va a poder usar este método si no ha ejecutado alguno (que pueda) antes. Al menos una de las propiedades de nuestro servicio tiene que tener esta propiedad a true.

IsTerminating
Si vale true el runtime de WCF terminará la sesión una vez ejecute el método y el cliente deberá crear una nueva conexión con el servicio una vez ejecute este método. El valor por defecto es false.

Para poder usar estas propiedades tenemos que cambiar otra propiedad, la propiedad SessionMode  del atributo ServiceContract de nuestro contracto. Para que funcionen estas propiedades el valor de SessionMode debe ser SessionMode.Required. Esta propiedad especifica si el servicio va a permitir o no sesiones robustas y fiables, pero no voy a entrar más en detalle, al menos en este post.

Para poner un pequeño ejemplo vamos a editar nuestro contrato:



<ServiceContract(SessionMode:=SessionMode.Required)> _
Public Interface IShoppingCart
    <OperationContract(IsInitiating:=True)> _
    Function AddItem(ByVal itemId As Integer) As Boolean
    <OperationContract(IsInitiating:=False)> _
    Function RemoveItem(ByVal itemId As Integer) As Boolean
    <OperationContract(IsInitiating:=False)> _
    Function GetShopingCart() As String
    <OperationContract(IsInitiating:=False, IsTerminating:=True)> _
    Function Checkout() As Boolean
End Interface

Antes de ejecutar el cliente debemos actualizar la referencia web, ya que al hacer esto la definición de la clase proxy que se crea para comunicarse con el servicio va a cambiar un poquito.

Si ahora descomentamos las líneas del cliente que ejecutan la función Checkout e intentan de nuevo acceder al servicio el sistema nos devolverá una excepción:

console


Lo que ha pasado es que al ejecutar la función Checkout, como esta marcada como IsTerminating, el servicio se ha destruido después de que el cliente ejecutara la función Checkout.

Si después de ejecutar al función Checkout volvemos a crear una conexión con el servicio, ya no da ese error:



‘…
Dim proxy As New ShoppingCartClient(«WsHttp_ShoppingCartEndpoint»)
Dim rand As New Random()
Dim result As Boolean = True
result = proxy.AddItem(rand.Next(0, 5))
result = proxy.AddItem(rand.Next(0, 5))
result = proxy.AddItem(rand.Next(0, 5))
result = proxy.AddItem(rand.Next(0, 5))
Console.WriteLine(proxy.GetShopingCart())
result = proxy.Checkout()
‘creamos una nueva conexión con el servicio
proxy = New ShoppingCartClient(«WsHttp_ShoppingCartEndpoint»)
proxy.AddItem(rand.Next(0, 5))
result = proxy.AddItem(rand.Next(0, 5))
Console.WriteLine()
Console.WriteLine(proxy.GetShopingCart())
‘…

console2


Si ahora intentamos ejecutar cualquier método del servicio sin haber ejecutado primero el método marcado como IsInitiating también nos da error; aunque la información de la excepción no es tan bonita como la de antes:


console3

Manteniendo el estado con WCF (1 de 2)

En muchos de los ejemplos que podemos ver sobre servicios web las llamadas a los diferentes métodos que proveen son independientes entre ellas. En la vida real es posible que necesitemos mantener el estado entre diferentes llamadas y que, además, éstas tengan que seguir una determinada secuencia.

El Framework 3.0 nos provee de formas muy fáciles para poder controlar estos casos, solo falta añadir unos atributos a los métodos de nuestro servico o al servicio en sí.

Para poder enseñar como hacerlo de una manera fácil de entender he creado un pequeño código de ejemplo.
He creado un servicio web que emula un carrito de compra; un ejemplo básico donde se debe mantener el estado entre diferentes llamadas. No he implementado ningún control de excepciones ya que así nos centramos en lo que toca.

Esta primera parte tratará de mantener el estado y concurrencia, en la otra entrada veremos como definir el orden de ejecución.

Por una parte tendremos la lista de productos; un xml, por otro el servicio web; con su contrato y la clase que lo implementa, una aplicación host; que sirve el servicio usando wsHttpBinding y, por último, el cliente; que consumirá el servicio.

Primero he creado un xml como contenedor de datos, que contiene una lista de productos. Lo he creado serializando un objeto de tipo List<Product>, así luego lo podremos deserializar y trabajar con él de una forma más fácil:



<?xml version=»1.0″ encoding=»utf-8″?>
<ArrayOfProduct xmlns:xsi=»http://www.w3.org/2001/XMLSchema-instance» xmlns:xsd=»http://www.w3.org/2001/XMLSchema»>
  <Product>
    <Id>0</Id>
    <Name>Agua</Name>
    <Price>0.5</Price>
  </Product>
  <Product>
    <Id>1</Id>
    <Name>Zumo de naranja</Name>
    <Price>1.2</Price>
  </Product>
  <Product>
    <Id>2</Id>
    <Name>Olivas</Name>
    <Price>2.35</Price>
  </Product>
  <Product>
    <Id>3</Id>
    <Name>Patatas</Name>
    <Price>1.75</Price>
  </Product>
  <Product>
    <Id>4</Id>
    <Name>Cerveza</Name>
    <Price>0.8</Price>
  </Product>
  <Product>
    <Id>5</Id>
    <Name>Refresco</Name>
    <Price>0.7</Price>
  </Product>
</ArrayOfProduct>

Luego he creado un proyecto tipo librería en Visual Basic y he creado las dos clases que voy a necesitar para tratar los datos: Product y CartItem.



Public Class Product
 
    Private _id As Integer
    Public Property Id() As Integer
        Get
            Return _id
        End Get
        Set(ByVal value As Integer)
            _id = value
        End Set
    End Property
 
    Private _name As String
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property
 
    Private _price As Decimal
    Public Property Price() As Decimal
        Get
            Return _price
        End Get
        Set(ByVal value As Decimal)
            _price = value
        End Set
    End Property
End Class
 
Public Class CartItem
    Private _product As Product
    Public Property Product() As Product
        Get
            Return _product
        End Get
        Set(ByVal value As Product)
            _product = value
        End Set
    End Property
 
    Private _cuantity As Integer
    Public Property Cuantity() As Integer
        Get
            Return _cuantity
        End Get
        Set(ByVal value As Integer)
            _cuantity = value
        End Set
    End Property
End Class

Después he definido el contrato: la Interface.



<ServiceContract()> _
Public Interface IShoppingCart
    <OperationContract()> _
    Function AddItem(ByVal itemId As Integer) As Boolean
    <OperationContract()> _
    Function RemoveItem(ByVal itemId As Integer) As Boolean
    <OperationContract()> _
    Function GetShopingCart() As String
    <OperationContract()> _
    Function Checkout() As Boolean
End Interface

Y la clase que la implementa: el servicio.



Public Class ShoppingCartSrv
    Implements IShoppingCart
 
    Private shoppingCart As New List(Of CartItem)
 
    Private Function getProduct(ByVal productId As Integer) As Product
        Dim products As New List(Of Product)
        Dim filename As String = «products.xml»
        If File.Exists(filename) Then
            Dim reader As New StreamReader(filename)
            Dim serializer As New XmlSerializer(GetType(List(Of Product)))
            products = CType(serializer.Deserialize(reader), List(Of Product))
            reader.Close()
        End If
 
        Dim product As Product = Nothing
        For Each p As Product In products
            If p.Id = productId Then
                Return p
            End If
        Next
        Return product
    End Function
 
    Public Function AddItem(ByVal itemId As Integer) As Boolean Implements IShoppingCart.AddItem
        For Each item As CartItem In shoppingCart
            If item.Product.Id = itemId Then
                item.Cuantity += 1
                Return True
            End If
        Next
        Dim product As Product = getProduct(itemId)
        If Not IsNothing(product) Then
            shoppingCart.Add(New CartItem With {.Product = product, .Cuantity = 1})
            Return True
        End If
        Return False
    End Function
 
    Public Function GetShopingCart() As String Implements IShoppingCart.GetShopingCart
        Dim list As String = «»
        Dim total As Double = 0
        For Each item As CartItem In shoppingCart
            list += String.Format(«Id: {0} Name: {1} Price: {2} Cuantity: {3}{4}», _
                                  item.Product.Id, item.Product.Name, item.Product.Price, item.Cuantity, Environment.NewLine)
            total += (item.Product.Price * item.Cuantity)
        Next
        If list.Equals(«») Then
            list = «Empty Cart»
        Else
            list += String.Format(«Total: {0} Euro», total)
        End If
        Return list
    End Function
 
    Public Function RemoveItem(ByVal itemId As Integer) As Boolean Implements IShoppingCart.RemoveItem
        Dim item As CartItem = Nothing
        For Each i As CartItem In shoppingCart
            If i.Product.Id = itemId Then
                item = i
            End If
        Next
        If Not IsNothing(item) Then
            shoppingCart.Remove(item)
            Return True
        End If
        Return False
    End Function
 
    Public Function Checkout() As Boolean Implements IShoppingCart.Checkout
        shoppingCart.Clear()
        Return True
    End Function
End Class

Ahora que ya tenemos creado el servicio, he creado una aplicación para que lo sirva. Creo un nuevo proyecto de consola, agrego la dos referencias que necesito: System.ServiceModel y al proyecto de librería que representa mi servicio. Y creo el código que necesito para servir el servicio:



Imports ShoppingCartService
Imports System.ServiceModel
 
Module Host
 
    Sub Main()
 
        Dim host As New ServiceHost(GetType(ShoppingCartSrv))
        host.Open()
        Console.WriteLine(«Service running»)
        Console.WriteLine(«Press ENTER to stop the service»)
        Console.ReadLine()
        Console.Write(«Closing…»)
        host.Close()
        Console.WriteLine(«Closed»)
 
    End Sub
 
End Module

Por último configuro cómo voy a servir el servicio con mi fichero de configuración app.config en la aplicación que hace de host:



<?xml version=»1.0″ encoding=»utf-8″ ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=»ShoppingCartService.ShoppingCartSrv» behaviorConfiguration=»MyBehavior»>
        <host>
          <baseAddresses>
            <add baseAddress=»http://localhost:9669/ShoppingCartService»/>
          </baseAddresses>
        </host>
        <endpoint address=»ShoppingCartSrv»
                  binding=»wsHttpBinding»
                  contract=»ShoppingCartService.IShoppingCart»
                  name=»WsHttp_ShoppingCartEndpoint»/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name=»MyBehavior»>
          <serviceMetadata httpGetEnabled=»true»/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Ahora solo falta el cliente.
Creo otra aplicación de consola para el cliente. Ejecuto la aplicación de Host para que se empiece a servir el servicio y poder, así, añadir la referencia al servicio al cliente con la ayuda de Visual Studio:

reference


Escribimos el código que consumirá el servicio: añadirá productos a la cesta y mostrará ésta por pantalla:



Imports Client.ShoppingCartService
 
Module Client
 
    Sub Main()
 
        Console.WriteLine(«Press ENTER when the service has started»)
        Console.ReadLine()
        Try
            Dim proxy As New ShoppingCartClient(«WsHttp_ShoppingCartEndpoint»)
            Dim rand As New Random()
            Dim result As Boolean = True
            result = proxy.AddItem(rand.Next(0, 5))
            result = proxy.AddItem(rand.Next(0, 5))
            result = proxy.AddItem(rand.Next(0, 5))
            result = proxy.AddItem(rand.Next(0, 5))
            Console.WriteLine(proxy.GetShopingCart())
            ‘result = proxy.Checkout()
            ‘Console.WriteLine(proxy.GetShopingCart())
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
        Console.WriteLine(«Press ENTER to finish»)
        Console.ReadLine()
 
    End Sub
 
End Module

Ahora ya tenemos preparado el entorno de nuestra aplicación: nuestro cliente y nuestro servicio. Ejecutémoslo a ver que le pasa al carrito, ¿se mantendrá el estado entre diferentes llamadas al servicio?, o sea: ¿al mostrar los elementos de la cesta estarán los añadidos anteriormente?…
(no os olvidéis de que tiene que estar ejecutándose la aplicación que hace de host del servicio antes de ejecutar el cliente)

first


…pues sí!.

Vamos a ver el por qué.

Si nos paramos a pensar qué debe estar pasando, el servicio crea una variable privada que es creada el crearse la instancia del servicio. Si dos clientes acceden al servicio el Framework crea una instancia del servicio para cada uno de ellos, así que cada uno de ellos va a tener un carrito de compra propio. La instancia del servicio se destruirá en el momento que el cliente cierre el proxy. Bueno,… cuando el Garbage Collector quiera. Pues ya está ¿no?, ya mantenemos el estado. Pero, ¿y si ahora tenemos 10.000 clientes?, tendremos 10.000 instancias del servicio corriendo: el cliente puede estarse media hora pensando qué comprar. No creo que pueda buscar muchos artículos en una máquina sin memoria.

¿Cómo podemos cambiar esto?, cambiando el ContextMode de nuestro servicio. Usando la propiedad InstanceContextMode del atributo ServiceBehavior de nuestro servicio:



<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> _
Public Class ShoppingCartSrv
    Implements IShoppingCart
‘…

o en C#



[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class ShoppingCartServiceImpl : IShoppingCartService
{
    //…

Esta propiedad puede tomar diferentes valores: InstanceContextMode.PerSession, InstanceContextMode.PerCall y InstanceContextMode.Single.

InstanceContextMode.PerSession
Es el que tiene por defecto. Mantiene una instancia por cliente y la destruye al desconectarse.

InstanceContextMode.PerCall
Crea una instancia solo para servir la petición de uno de sus métodos y la destruye una vez ejecutado.

InstanceContextMode.Single
Crea una instancia la primera vez que se llama al servicio y usa la misma para todos los clientes.

Ahora ya sabemos por qué se mantenía el estado. Pero al ver esto, se nos pueden plantear preguntas respecto a la concurrencia. Si un cliente crea dos «threads» que hagan diferentes llamadas a su servicio, ¿qué hará el servicio?, si le llega una llamada a un método que está ejecutando mantendrá a la llamada «en espera» y, posiblemente el cliente termine por time-out.

Para poder indicarle al servicio como tratar peticiones concurrenciales  tenemos otra propiedad del atributo ServiceBehavior llamada ConcurrencyMode. Y puede tomar dos valores ConcurrencyMode.Single (por defecto el servicio usa esta) o ConcurrencyMode.Multiple. Con la segunda opción el servicio será capaz de aplicar concurrencia a las peticiones del cliente.

Podemos probar ahora de cambiar la propiedad InstanceContextMode a nuestro servicio y ver como el resultado del cliente va a cambiar. Si aplicamos InstanceContextMode.PerCall veremos que al imprimir el carrito este sale vacío. En cambio, si usamos InstanceContextMode.Single veremos que aunque cerremos el proxy y abrimos otro, o ejecutamos dos o más clientes, éstos comparten el carrito y entre todos van añadiendo elementos.

Con los diferentes valores de InstanceContextMode podemos controlar el tiempo de vida de nuestro servicio. Esta propiedad es global a todos los servicios y es el Framework el que se encarga de crear o destruir el servicio, pero no tiene en cuenta qué método esta ejecutando el cliente.
Podemos controlar cuando se destruye la instancia con una la propiedad ReleaseInstanceMode del atributo OperationContract de nuestros métodos definidos en el contrato.

Esta propiedad puede tomar cuatro valores:

ReleaseInstanceMode.AfterCall
Una vez el cliente acaba de utilizar el método, el runtime de WCF libera los recursos del servicio para que lo destruya el Garbage Collector. Si el cliente invoca otro método el runtime creará una instancia nueva del servicio.

ReleaseInstanceMode.BeforeCall
Si existe alguna instancia del servicio cuando el cliente invoca el método el runtime, primero destruye esa instancia y luego crea una nueva para servir la petición.

ReleaseInstanceMode.BeforeAndAfterCall
La combinación de las dos anteriores.

ReleaseInstanceMode.None
Este es el valor por defecto de esta propiedad. La instancia del servicio es creada y destruida de acuerdo con el valor de la propiedad InstanceContextMode del atributo ServiceBehavior del servicio.

La idea es jugar con el código del cliente y las propiedades del servicio, y ver como reacciona a los diferentes valores que pueden tomar las propiedades.

Hacer un "Trace" de un Servicio de WCF

Siguiendo con WCF vamos a ver como hacer un «trace» de las comunicaciones de nuestro servicio.

Gracias a la forma de trabajar con WCF no va a hacer falta tirar ni una línea de código; solo tenemos que modificar el fichero de configuración de nuestro servicio.

Lo que vamos a tener que indicar es qué queremos guardar, cómo lo queremos hacer y dónde lo vamos a guardar.

Para este ejemplo tenemos un servicio web muy simple; solamente tiene un método que devuelve la fecha actual. Y el servicio web lo sirve una aplicación que hace de host. Tiene dos endpoints: uno para el puerto 8000 usando un basicHttpBinding y uno para el puerto 8080 usando un netTcpBinding. Solo vamos a hacer el tracing para el host, así que solo vamos a modificar el app.config del host.

Sin aplicar el «tracing» el archivo app.config del servicio tiene esta pinta:



<?xml version=»1.0″ encoding=»utf-8″ ?>
<configuration>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration=»MyConf»
               name=»XService.Service»>
        <endpoint address=»Service»
                  binding=»basicHttpBinding»
                  name=»XBasicHttpBindingEndpoint»
                  contract=»XService.IService» />
        <endpoint address=»Service»
                  binding=»netTcpBinding»
                  name=»XNetTcpBindingEndpoint»
                  contract=»XService.IService» />
        <host>
          <baseAddresses>
            <add baseAddress=»http://localhost:8000/XService» />
            <add baseAddress=»net.tcp://localhost:8080/XService» />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name=»MyConf»>
          <serviceDebug includeExceptionDetailInFaults=»true»/>
          <serviceMetadata httpGetEnabled=»true»/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Lo primero que haremos es definir el qué: qué es lo que queremos guardar. Para hacerlo usaremos la etiqueta messageLogging de diagnostics, que está dentro del espacio de nombres (etiquetas) <system.serviceModel>



<system.serviceModel>
   <diagnostics>
     <messageLogging
       logEntireMessage=»true»
       logMessagesAtServiceLevel=»true»/>
   </diagnostics>
    <!– … –>
</system.serviceModel>

El campo logEntireMessage indica que queremos que se guarde todo el cuerpo del mensaje, si no solo se guardaría la cabecera. La opción logMessagesAtServiceLevel guardará los mensajes tal y como salen del servicio y llegan al servicio.
Hay más opciones a escoger: podemos guardar los mensajes a nivel de la capa de transporte, el número de mensajes máximo, etc.

Ahora vamos a definir el cómo y el dónde. Para hacerlo usaremos la librería de <system.diagnostics>.
La información de tracing es recibida por las sources. En nuestro caso usaremos una única source del tipo System.ServiceModel.MessageLogging, que guarda mensajes. Otra opción es la source System.ServiceModel que guarda eventos del servicio como cuando empeza a escuchar cuando se cierra, etc. Luego le añadimos un listener que definiremos luego, así que por ahora tenemos esto:



<system.diagnostics>
    <sources>
      <source name=»System.ServiceModel.MessageLogging»
              switchValue=»Verbose»>
        <listeners>
          <add name=»MyListener»/>
        </listeners>
      </source>
    </sources>
    <!– … –>
  </system.diagnostics>

Ahora tenemos que definir el listener al que hemos llamado MyListener:



<system.diagnostics>
    <!– … –>
    <sharedListeners>
        <add initializeData=»C:app_tracelog.svclog»
           type=»System.Diagnostics.XmlWriterTraceListener»
           name=»MyListener»
           traceOutputOptions=»None»/>
    </sharedListeners>
</system.diagnostics>

Aquí le indicamos dónde guardar los datos, qué tipo de Listener utilizar y qué opciones quieres usar. Las opciones sirven, por lo que he visto (aún me lo tengo que mirar mejor) , para correlar diferentes peticiones y respuestas de diferentes clientes. En nuestro caso solo tenemos un cliente y no tenemos activada ninguna opción.

Con esto ya tenemos echo nuestro tracing y podemos probarlo.

A nivel de código no tenemos que tocar nada así que ejecutamos nuestro servicio y nuestro cliente como siempre. Cada vez que el cliente y el servicio se comuniquen se guardarán las entradas correspondientes en nuestro archivo de log.

Para poder ver cómodamente el archivo podemos usar una utilidad que viene cuando te instalas el SDK de Windows llamada «Service Trace Viewer». Si abrimos el fichero con el programa veremos algo así:

trace


A la izquierda podemos escoger las diferentes entradas del log. Ahora solo tenemos dos: la petición y la respuesta. Y a la derecha podemos ver el contenido de la entrada. Si seleccionamos la pestaña de XML podemos ver el mensaje enviado por el cliente haciendo la petición y la respuesta del servicio.


peti


Y la respuesta:


resp


Ahora podéis hacer diferentes pruebas. Por ejemplo, en este caso usaba el binding por net-tcp, si lo usáis con basicHttp podréis ver la cabecera HttpRequest y la del mensaje SOAP.
Podéis hacer un trace a nivel de transporte o añadir seguridad y ver como el mensaje primero está encriptado y luego se desencripta, etc.

En este ejemplo hemos editado el archivo de configuración de nuestro servicio a mano, pero hay una utilidad que te ayuda bastante a hacerlo: Service Configuration Editor. Con Visual Studio 2005 y las extensiones para .NET Framework 3.0 podías acceder a él apretando el botón derecho del mouse encima del fichero .config que querías modificar y escoger la opción adecuada. Con Orcas debes abrirla a parte o, al menos yo no la he encontrado. Está dentro de las utilidades del SDK de Windows. Yo la he añadido al menu Tools del Visual Studio Orcas por comodidad.

Redirigir formularios en ASP.NET (Mejorado)

Poco después de colgar mi anterior post (Redirigir formularios en ASP.NET) un comentario de dvilchez me ha enseñado que hay una forma mejor y más correcta de redirigir formularios en ASP.NET.


Yo lo hacía como he explicado en mi primer post porque no sabía de la existencia de la otra manera. Como ya sabía como hacerlo, no miré o busqué una forma mejor de hacerlo (un error por mi parte).


Pero reconozo que su forma es mucho mejor así que aquí os dejo el enlace donde lo explica:
http://www.c-sharpcorner.com/UploadFile/DipalChoksi/xpgpostbk_asp2_dc08102006235543PM/xpgpostbk_asp2_dc.aspx?ArticleID=9e2b12e6-7842-419d-a3c9-df58f6aba523


Muchas gracias dvilchez!!

Redirigir formularios en ASP.NET

Por defecto una página ASP.NET, al usar controles ASP.NET estos funcionan a base de eventos. Cuando hacemos click en un botón de una página web, esta se carga de nuevo y se ejecuta el método enlazado al evento del botón Click.

Las páginas en ASP.NET contienen un solo formulario con el atributo id y el runnat=»server». Al generarse estos eventos la página se llama a ella misma para poder capturarlos y ejecutar la función asociada. Eso quiere decir que el atributo action del formulario apunta a la página que lo contiene pero, ¿y si queremos que nuestro formulario se redirija a una página diferente?. Hay diferentes soluciones para esto. Una de ellas es hacer lo que hemos hecho siempre los desarrolladores web, coger nuestro formulario, hacerlo en html y hacer que action apunte a la página que queramos. Otra forma és recoger todos los datos en el evento Click del botón, ponerlos en la variable de estado Session, o en la QueryString si se puede y redirigir al usuario a la otra página con Response.Redirect(). Personalmente de estas dos prefiero la primera, aunque hay otra manera que, usando los formularios de ASP.NET, también conseguimos los mismo de una manera más limpia. Solo tenemos que poner una pequeña función en Javascript.

La primera cosa que debemos hacer desde nuestra función en javascript es cambiar una variable llamada __VIEWSTATE. Cuando una página de ASP.NET recibe un post mira la variable __VIEWSTATE, ASP.NET usa esta variable para muchas cosas que ahora no vienen al caso, pero una de ellas es que si dejamos que se redirija nuestra página con el __VIEWSTATE que tenía no va a poder validar la página y nos dará un mensaje de error parecido a este: «The viewstate is invalid for this page and might be corrupted.». Si intentamos borrar los datos también dará el mismo error. Lo que haremos será renombrar la variable a otro nombre que no moleste, así la página que reciba la información no recibirá ningún __VIEWSTATE y no dará error. Ahora solo nos falta redirigir la respuesta a la página que queramos cambiando el atributo action del formulario.

Así que nos crearemos una función en Javascript que hará justamente esto:

function RedirigirA(nombreDeLaPagina)
{
    document.forms[0].action = nombreDeLaPagina;
    document.forms[0].__VIEWSTATE.name = 'lala';
}

Ahora solo falta decirle que ejecute ésto al submitir la página en el método Page_Load de nuestra página:


private void Page_Load(object sender, System.EventArgs e)
{
    Submit.Attributes.Add("onclick", "RedirigirA('OtraPagina.aspx');");
}

En este caso he dado por supuesto que cualquier evento de Postback hará que la página se redirija al otro formulario pero, en otros casos, a lo mejor solo queremos que se haga al clicar en ciertos botones. Solo habrá que añadir la función en el lugar correcto.

Ahora ya podemos, desde la página que recibe el formulario, acceder a los datos desde C# como siempre:

private void Page_Load(object sender, System.EventArgs e)
{
    Result.Text = Request.Form["MiParametro"].ToString();
}

Espero que os sea útil algún día.

Métodos extensores y operador ternario

Una de las novedades que más me ha gustado de las nuevas versiones de nuestros lenguajes son los métodos extensores.

Es una particularidad que yo he usado algunas veces en javascript y que consistía en añadir un nuevo método al prototype de una clase, para que todas las instancias de una clase lo tuvieran. Esto es bastante normal en la programación orientada a objetos al crear nuestras propias clases, pero lo bueno es que podemos hacerlo también con las clases del Framework: por ejemplo con la clase String.

Para hacerlo la forma difiere un poco al hacerlo en C# que en Visual Basic pero ambos lenguajes ya soportan esta funcionalidad.

C#

Para crear un método extensor en C# usamos la palabra reservada this al primer parámetro del método. Además solo se pueden declarar dentro de clases estáticas.

Si nos creamos nuestra librería, definiríamos nuestro método extensor así:

using System;
 
namespace MetodosExtensores
{
    public static class Extensiones
    {
        public static int ElevarA(this int num, int potencia)
        {
            return (int)Math.Pow(num, potencia);
        }
    }
}

Ahora en nuestro programa, podemos usar estos métodos solo importando la librería:

using System;
using MetodosExtensores;
 
namespace Miprograma
{
    class Program
    {
        static void Main(string[] args)
        {
            int num = 2,potencia = 5;
            int resultado = num.ElevarA(potencia);
            //Equivalente a hacer esto:
            //int resultado = Extensiones.ElevarA(num, potencia);
            Console.WriteLine("{0} elevado a {1} es {2}", num, potencia, resultado);
            Console.ReadLine();
        }
    }
}

Visual Basic .NET

En este caso lo que debemos usar es un atributo antes del método para indicar que va a ser un método extensor. Además solo pueden estar definidos dentro de módulos:

Namespace MetodosExtensoresVB
    Public Module Extensiones
        <System.Runtime.CompilerServices.Extension()> _
        Public Function ElevarA(ByVal num As Integer, ByVal potencia As Integer)
            Return CType(Math.Pow(num, potencia), Integer)
        End Function
    End Module
End Namespace

Como vemos puede sernos de gran utilidad en algunas ocasiones.

Además también han añadido una nueva funcionalidad en Visual Basic 9: el Operador Ternario.

Lo que todos los programadores en C# y en C conocíamos como:

string msn = a > b ? "a>b" : "a<=b";

Ahora también lo podemos hacer en Visual Basic así:

Dim msg as String = If( a>b ? "a>b", "a<=b")

Poco a poco nuestro lenguajes van mejorando y haciéndonos la vida más fácil.

Tutorial WCF (5 de 5)

Artículos anteriores:
Tutorial WCF (1 de 5)
Tutorial WCF (2 de 5)
Tutorial WCF (3 de 5)
Tutorial WCF (4 de 5)

5 – CREANDO UN CLIENTE


La forma más fácil i rápida de crear un cliente/consumidor de un servicio de WCF es con la ayuda de Visual Studio. La forma de hacerlo es muy parecida a cómo lo hacíamos cuando se trataba de un Servicio Web del Framework 2.0: en nuestro proyecto donde queremos consumir un servicio tenemos la opción de Add Service Reference, pero en este caso la dirección que indicaremos no solo tiene porque ser http://…; también podemos introducir los diferentes protocolos net.tcp://…, net.pipe://… o net.msmq://… Al introducir una dirección correcta Visual Studio utiliza una herramienta que lee la metadata que le brinda el servicio (el contrato WSDL) y genera clase proxy que nos comunicará con el servicio. Podemos ver el código generado expandiendo la carpeta Service References en el explorador de soluciones. La herramienta utilizada por el Visual Studio para generar la clase proxy es el svcutil. Podemos generar nosotros mismos la clase proxy desde línea de comandos del SDK (Start->All programs->Microsoft Windows SDK->CMD Shell) (debemos tener el SDK de Windows instalado) utilizando la siguiente instrucción: (debemos ir primero al directorio donde queremos que se nos genere la clase)


svcutil http://localhost:8000/BlogWCF /out:Cliente.cs /config:App.config


Con esta línea le estaríamos diciendo que buscara un servicio que se encuentra en la dirección http://localhost:8000/BlogWCF, que leyera la metadata y generara la clase proxy en el fichero Cliente.cs y que la configuración de los bindings y enpoints necesaria para comunicarse con el cliente la guardara en un fichero llamado App.config.


Ahora ya sabemos como conseguir generar el código necesario para comunicarme con nuestro servicio. Vamos a generar un cliente que lo consuma y muestre algo por pantalla:


Creamos una proyecto de aplicación de consola y lo llamamos ClienteWCF. Yo lo he hecho en Visual Basic para cambiar un poco, pero eso va a gustos. Vamos a crear el proxy para el servicio de la forma rápida: con Visual Studio. Primero hacemos correr el servicio; si queremos usar el del servicio web ya estará corriendo en el IIS, si usamos el que se servía desde una aplicación Host debemos ejecutarla y dejarla funcionando. Hacemos click con el botón derecho encima del nombre de nuestro proyecto «cliente» y seleccionamos Add Service Reference. Ahora debemos colocar la dirección donde se encuentra escuchando nuestro servicio net.tcp://…, o http://…., le damos un nombre al espacio de nombres donde se va a crear nuestra clase proxy y le damos a Go. Nos mostrará una pantalla parecida a esta:


cliente


Le damos a OK y ya tenemos creada la clase proxy. Ahora ya podemos crear nuestro cliente, importar el espacio de nombres que le hemos dicho y empezar a utilizar nuestro cliente. El nombre de la clase proxy que invoca los métodos de nuestro servicio es el nombre de nuestro servicio acabado en Client: en nuestro caso MiServicioClient. Ahora podemos hacer que nuestro cliente invoque los métodos de nuestro servicio a través de la clase generada:



Imports ClienteWCF.BlogWCFReference
 
Module Module1
 
    Sub Main()
 
        Console.WriteLine(«Una vez este iniciado « + _
            «el servicio presione una tecla para continuar.»)
        Console.ReadKey(True)
 
        Dim cliente As New MiServicioClient()
        Dim cumples As Cumple() = cliente.DameCumples()
        For Each cu As Cumple In cumples
            Console.WriteLine(«El cumpleaños de {0} es el {1}», _
                    cu.Nombre, cu.FechaCumple.ToShortDateString())
        Next
 
        Console.WriteLine()
        Console.WriteLine(«{0} tiene ahora {1} años», _
                          cumples(0), _
                          cliente.DimeEdad(cumples(0)))
 
        Console.WriteLine()
        Console.WriteLine(«Presiona una tecla para terminar.»)
        Console.ReadKey(True)
 
    End Sub
 
End Module

Para poder ejecutarlo el servicio debe de estar activado. Si no usamos el IIS como host sino una aplicación y además está en la misma solución, podemos decirle al Visual Studio que ejecute ambos proyectos a la vez: botón derecho en el nombre de nuestro solución y seleccionamos la opción Set StartUp Projects…. Escogemos la opción de Multiple stratup projects: , ponemos Start en Action tanto en el cliente como en la aplicación Host y aceptamos. Si ahora le damos a ejecutar se abrirán dos consolas: una para el cliente y otra para el host. Primero hacemos que empiece a ejecutarse el servicio y luego la aplicación cliente; el resultado debe ser parecido al siguiente:

addreference

Ahora ya sabemos como consumir un servicio de WCF.


Si ahora queremos que lo consuma usando el protocolo TCP no tenemos que hacer nada más que modificar el app.config del cliente. Si lo abrimos veremos que nos ha generado un archivo de configuración parecido a los de antes pero con algunas cositas más. Vemos que, por ejemplo, no solamente le dice que tipo de binding tiene el endpoint si no que también tiene una configuración especial para este binding. Como nosotros no indicamos nada especial en el App.config del servicio (cogimos la configuración por defecto de cada uno de los dos tipos de binding) y el cliente tiene el app.config que ha generado leyendo la metadata de nuestro servicio, los parámetros que hay son los mismos que los nuestros, los que hay por defecto. Podemos modificar el app.config para que acceda mediante TCP modificando solo la línea del endpoint:



<endpoint address=»http://localhost:8000/BlogWCF/Servicio»
                      binding=»basicHttpBinding»
                      bindingConfiguration=»BasicHttpBinding_IMiServicio»
                      contract=»BlogWCFReference.IMiServicio»
                      name=»BasicHttpBinding_IMiServicio» />

La cambiamos por esta otra:



<endpoint address=»net.tcp://localhost:8080/BlogWCF/Servicio»
                    binding=»netTcpBinding»
                    contract=»BlogWCFReference.IMiServicio»
                    name=»NetTcpBinding_IMiServicio» />

Si volvemos a ejecutar ahora el servicio y el cliente podemos ver que vuelve a funcionar; pero esta vez esta accediendo mediante el protocolo TCP. Como vemos tenemos completamente separadas la parte de comunicaciones de la de implementación.


Con esto acaba esta serie de posts/tutorial sobre como utilizar Windows Communication Foundation para crear, servir y consumir servicios. Espero que os haya ayudado.


Más adelante iré posteando cosas más avanzadas sobre WCF: servicios, bindings personalizados, seguridad, etc. Hasta pronto.

Tutorial WCF (4 de 5)

Artículos anteriores:
Tutorial WCF (1 de 5)
Tutorial WCF (2 de 5)
Tutorial WCF (3 de 5)

4- APLICACIÓN HOST


Otra forma de servir un servicio de WCF es hacer que una aplicación haga de host del servicio y sea ésta la que tenga el control sobre éste. En este tutorial haremos que sea una aplicación de consola pero podría, tranquilamente, ser cualquier otro tipo de aplicación como, por ejemplo, un servicio de Windows.


Partiremos del servicio creado en el segundo post de este tutorial, el que no tiene ningún archivo de configuración ni el .svc; ya que éstos son para servirlo mediante IIS. En este caso el archivo de configuración lo tendrá la aplicación que hará de host.


Empezamos creando un nuevo proyecto de consola que llamaremos Host y al que importaremos dos dlls .La primer será la necesaria para trabajar con WCF; System.ServiceModel, y la segunda será la que genera nuestro proyecto del servicio de WCF; BlogWCF.dll. En el archivo Program.cs añadiremos los using/import de ambas dlls y escribiremos el código necesario para que arranque el servicio:



using System;
using System.Collections.Generic;
using System.ServiceModel;
using BlogWCF;
 
namespace Tests
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost hos = new ServiceHost(typeof(MiServicio));
            Console.WriteLine(«Presiona cualquier tecla para empezar»);
            Console.ReadKey(true);
            try
            {
                hos.Open();
                Console.WriteLine(«El servicio se esta sirviendo correctamente… «);
                Console.WriteLine(«presiona cualquier tecla para cerrarlo.»);
                Console.ReadKey(true);
                hos.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(«ERROR: «, ex.Message);
                Console.WriteLine(ex.Message);
            }
            Console.ReadKey(true);
        }
    }
}

Vemos que lo primero que hace es crear una instancia de la clase ServiceHost indicándole a ésta que tipo de servicio va a usar. Luego, entre un bloque try/catch por si surge algún problema, hacemos que empiece a servir-se el servicio (válgase la redundancia), y lo cerramos una vez usado.


Pero con esto no estamos aun, falta definir como va a ser el canal de comunicación y como se va a acceder al servicio. Para ello añadiremos un fichero de configuración a nuestra aplicación Host. Como en este caso sí que se trata de una aplicación normal y no de una aplicación web añadiremos un archivo App.config con la siguiente información:



<?xml version=»1.0″ encoding=»utf-8″ ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=»BlogWCF.MiServicio»  behaviorConfiguration=»lala»>
        <host>
          <baseAddresses>
            <add baseAddress=»http://localhost:8000/BlogWCF»/>
          </baseAddresses>
        </host>
        <endpoint address=»Servicio»
                  binding=»basicHttpBinding»
                  contract=»BlogWCF.IMiServicio»/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name=»lala»>
          <serviceMetadata httpGetEnabled=»true»/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Como vemos es casi igual al que teníamos antes para el Web.config pero cambiando el puerto de destino y la forma de definir las direcciones. El puerto que usaba antes era el 80, el puerto por defecto para comunicaciones mediante http. Si no ponemos un puerto diferente (y libre) nos dará un error, ya que estaremos intentando servir por un puerto que lo esta usando otra aplicación, en nuestro caso el IIS. Las direcciones se generaran en este caso concatenando la baseAddress específica a un protocolo con la address del endpoint. En este caso solo tenemos una y uno, y no hay donde perderse, pero puede haber más de una baseAddress más de un endpoint. La forma de concatenarse dependerá del binding al protocolo utilizado: en este caso en basicHttpBinding busca la baseAddress que empiece por http.


Ahora ya tenemos todo preparado para hospedar nuestro servicio. Si todo esta bien y ejecutamos nuestra aplicación tiene que salir esta pantalla:


console1


Si mantenemos el servicio sin cerrarlo y abrimos una nueva ventana de comandos (Inicio->Ejecutar->’cmd’) y escribimos el comando netstat -a -n veremos que nos aparece que hay un servicio ejecutandose en el puerto 8000: el nuestro. Si quitamos la opción -n veremos que protocolo usa (http).

console2

Si queremos además servir nuestro servicio también con el protocolo tcp por el puerto 8080 solo debemos editar el App.config de la manera siguiente:



<?xml version=»1.0″ encoding=»utf-8″ ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=»BlogWCF.MiServicio»  behaviorConfiguration=»lala»>
        <host>
          <baseAddresses>
            <add baseAddress=»http://localhost:8000/BlogWCF»/>
            <add baseAddress=»net.tcp://localhost:8080/BlogWCF»/>
          </baseAddresses>
        </host>
        <endpoint address=»Servicio»
                  binding=»basicHttpBinding»
                  contract=»BlogWCF.IMiServicio»/>
        <endpoint address=»Servicio»
                  binding=»netTcpBinding»
                  contract=»BlogWCF.IMiServicio»/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name=»lala»>
          <serviceMetadata httpGetEnabled=»true»/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

En este caso tenemos dos baseAddress y dos enpoints: se concatenarán las direcciones por el protocolo. Si os fijáis la baseAddress que se refiere al protocolo tcp empieza por net.tcp. Si ejecutamos y volvemos a mirar el netstat veremos que tenemos el servicio sirviéndose por dos puertos diferentes y con protocolos diferentes:


console3


Con esto ya hemos visto como podemos servir nuestros servicios y añadir enpoints con diferentes mecanismos de comunicación a cada uno.


Ahora falta ver como consumirlos, pero esto será en el siguiente post: Consumiendo un Servicio de WCF.

Turorial WCF (3 de 5)

Artículos anteriores:
Tutorial WCF (1 de 5)
Tutorial WCF (2 de 5) 


3 – BINDINGS


Nuestro servicio dará una funcionalidad pero el cliente tiene que acceder a él de alguna manera. Lo que tenemos que hacer es configurar de qué manera se va a comunicar el el servicio y dónde estará éste. Los servicios podemos tenerlos accesibles en diferentes lugares (puertos) y usando diferentes mecanismos de comunicación. A cada combinación de lugar/mecanismo le llamaremos endpoint.


Los Bindings especifican los mecanismos de comunicación que usaremos para «hablar» con un endpoint.
La plataforma .NET nos ofrece una serie de bindings «de serie» que podemos configurar y usar. Con estos definiremos como queremos que se codifiquen los datos, la seguridad de las comunicaciones, la compresión, el protocolo a usar, etc.


Nosotros podemos crear nuestros propios bindings si nuestro proyecto usa algún protocolo propio o algún tipo de mecanismo de comunicación diferente a los que nos da el Framework.


Podéis encontrar la lista de bindigns que nos da la Plataforma .NET en el siguiente enlace: http://msdn2.microsoft.com/en-us/library/ms731092.aspx


4 – CONFIGURANDO NUESTRO SERVICIO


Lo primero que vamos a hacer es servir nuestro servicio de WCF creado en el post anterior como un servicio Web a través de SOAP y XML. Para esto necesitamos tener el Internet Information Services instalado.


Primero debemos cambiar la carpeta donde se generan las dlls de nuestro servicio. Por defecto se generan el la carpeta bin/Debug, pero si ponemos el servicio en el IIS, éste va a leer directamente de bin así que debemos cambiar el lugar. Para hacerlo hacemos click con el botón derecho en nuestro proyecto y vamos a propiedades. En el apartado Build, en Output cambiaos el Output path a bin, guardamos y listos.


Ahora debemos crear un fichero de configuración para configurar el servicio y sus endpoints. Como la aplicación va a estar servida por el IIS, o sea, va a ser una aplicación web, la configuración va dentro del archivo Web.config. Lo creamos (si lo creamos con la plantilla borramos todo su interior) y escribimos lo que necesitamos:



<?xml version=»1.0″?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=»BlogWCF.MiServicio»>
        <endpoint address=»»
                  binding=»basicHttpBinding»
                  contract=»BlogWCF.IMiServicio» />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Aquí hemos definido un solo servicio con un solo enpoint, pero podríamos tener varios y dependiendo de a que endpoint se dirigiera el cliente usaría un binding o un servicio diferente. En el atributo name del servicio hemos colocado el espacio de nombres de nuestro servicio seguido del servicio. En el endpoint no hemos colocado ninguna dirección ya que será en el lugar donde lo sirva el IIS. El binding utilizado es el basicHttpBinding (ojo, es case sensitive!); éste nos provee el sistema básico de comunicaciones mediante Web Services. Después le hemos dicho qué contrato implementa este servicio.

Ahora lo único que falta es un ficherito con extensión .svc para que el usuario tenga un punto a donde navegar y el IIS sepa que ésto que está sirviendo es un servicio de WCF y sepa como compilar y generar la información WSDL del contrato cuando el cliente se lo pida. Añadimos un nuevo fichero de texto y lo nombramos Service.svc.



<%@ServiceHost Service=»BlogWCF.MiServicio» %>

Como vemos sigue el patrón de los fichero de ASP.NET, una directiva con <%@, en este caso ServiceHost, que indica que servico tiene que compilar y ejecutar.
Con esto ya podemos poner nuestro servicio en nuestro IIS y navegar por Internet para verlo. Sin muchos detalles: en el IIS creamos un directorio virtual que apunte al directorio raíz donde está en .svc y listos. Accedemos al servicio con nuestro navegador: http://localhost/BlogWCF/Service.svc (mi directorio virtual se llama BlogWCF) y tenemos que ver algo parecido a esto:

wcfnowsdl

Si nos fijamos hay una línea que pone: Metadata publishing for this service is currently disabled. Por seguridad el IIS deshabilita la posibilidad de publicar la metadata. Si queremos verla y que clientes desde fuera puedan acceder a ella para poder ver el WSDL debemos modificar un poco nuestro Web.config. Crearemos un behavior que extenderá la funcionalidad de nuestro servicio:



<?xml version=»1.0″?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=»BlogWCF.MiServicio»
               behaviorConfiguration=»MiBehavior»>
        <endpoint address=»»
                  binding=»basicHttpBinding»
                  contract=»BlogWCF.IMiServicio» />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name=»MiBehavior»>
          <serviceMetadata httpGetEnabled=»true»/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Si volvemos a cargar la pagina veremos que ahora sí podemos acceder a la metadata que genera a partir del contrato (WSDL), donde se define qué funcionalidades da nuestro servicio, qué tipos de datos usa, y cómo se accede.

wcfwithlinkwsdl

Con esto ya tenemos listo nuestro servicio web.

También podemos hacer que nuestro servicio sea servido por otro programa y con otro protocolo.


Ésto lo veremos en el próximo post: Sirviendo un Servicio de WCF (2ª parte).

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.