DevCamp: volviendo al pasado y viendo el futuro.

Ya estoy de vuelta a casa.

Como siempre que vengo de uno de lo eventos con la comunidad de estudiantes, vengo feliz de ver en que se está convirtiendo una pequeña idea, y de conocer a nuevos amigos, a nuevos profesionales y de compartir con ellos unas buenas horas de conocimiento, experiencias, cervezas y risas.

Por que lo más importante y emocionante que te puedes llevar de un evento de Microsoft no son las tecnologías que te enseñan, no son los grandes avances que estás creando y no, no son los regalillos que nos dan; son las personas con las que compartes la velada, con las que te tomas el café entre sesión y sesión, o con las que te metes en fregaos durante las actividades que se organizan. Y son estas personas las que te hacen volver a despertar el friki que tenemos dentro, las que te devuelven las ganas e ilusión que con el tiempo y los años vas perdiendo, las que las largas jornadas de trabajo merman y esconden. Y es a estas personas a las que les dos las gracias.

Animar también a los nuevos estudiantes que se animan a meterse en este mundo, animarles a no solo aprender de nosotros, si no que nos enseñen y demuestren hasta donde podríamos haber llegado, que nos metan caña y que se conviertan el los mejores profesionales y las mejores personas de nuestra casa. Aprended, enseñad y compartid. Demostremos a Redmond que, aunque allí se desarrolle el software de Microsoft, es aquí donde en realidad, esta su corazón y su fuerza.

CIMG2803.JPGCIMG2821.JPG
Aquí os dejo un enlace a las foticos de la gymkana
Hasta pronto!

.NET… todos crecemos.

Parece que fue ayer cuando Ricardo me dio el libro de C# en Valencia y nos explicó qué era .NET. Era a principios de Octubre del 2004 durante el CNR del IEEE de ese año.

Ahí se quedo la cosa; un libro y una ilusión.

Luego la ilusión se volvió más grande y juntos montamos una pequeña presentación de .NET en mi universidad, la UPC. Descubrí que no estaba solo, que había más gente que, como yo, no solo estaba en la universidad para estudiar, si no que tenía ganas de aprender cosas nuevas y emprender una nueva aventura junto a mi: montamos nuestro Club .NET en la UPC, mientras que Ricardo montó el suyo en Valencia. Aun recuerdo esa ilusión y esas ganas de comerme el mundo que tenía, las ganas de compartir con todos lo que estaba aprendiendo y de aprender más y más.

La cosa salió muy bien, Microsoft nos apoyó y nos llenó de recursos. Más clubes empezaron a aparecer por toda las península e incluso en las islas.

Recuero también en segundo año, hacia principios del 2006 cuando me quedé boquiabierto al ver en que se había convertido lo que habíamos empezado y como estudiantes de toda España me saludaban y querían conocerme.

Pero el tiempo pasa volando. Ya llevo un tiempo trabajando como desarrollador en .NET y las ilusiones y ganas de hacer eventos y actividades se dividen. Queda el amor por mi club, el que creé e intenté llevar los mejor posible con la ayuda de mis amigos: gracias Chema!, gracias Jordi, gracias Jose!,… Ya no es como antes, después de trabajar durante todo el día sobre .NET ya no hay las mismas ganas seguir desarrollando y descubriendo, aunque aun no han desaparecido.

Ahora me encuentro aquí en Guadalajara, en la habitación del hotel TRYP Guadalajara, en medio del DevCamp, a punto de acostarme y con mi nuevo MacBook encima las piernas. Recordando las ganas e ilusión que tenía y viéndolas en los ojos de los estudiantes nuevos que se quieren meter en esta aventura. También es bonito de ver a los veteranos de los Clubs .NET, a los que siempre están aquí, dando la cara y rompiéndose la espalda para ser los mejores.

Gracias familia…, gracias .NET.

TechEd – Spanish People Night

Ayer por la noche se organizaron diferentes eventos para los participantes del TechEd de cada país. Nosotros, los españolitos, fuimos a cenar a un restaurante del Diagonal Mar.

Yo me pude escapar y bajar a Barcelona para estar con mis compañeros por última vez durante este TechEd.

Vino muchísima gente; los chicos de MS, MVP’s, MSP’s, estudiantes, profesionales y demás fauna española. Me hizo ilusión ver a mis colegas de BcnDev (Buenas José Luís!), a Alfonso, a Rodrigo Corral, a David y a Miguel.







CIMG0208 CIMG0211


Como tenía que ser, compartí mesa con mis colegas de los clubes y nos llenamos de comida, de eso sobró cantidad. 🙂 Hubo unas discusiones sobre política… , na, que me lo pase genial, como siempre que estoy con gente como vosotros. 🙂

Luego nos fuimos a tomar algo a la fiesta que se montó en Catwalk.






CIMG0218 CIMG0217

Lástima que me tuve que ir pronto, ya que vivo en Sant Cugat, y el último tren salía a las 00:04.

En flickr dejo las fotos de ayer, un bsote a tod@s.

Ojalá pudiera venir con vosotros los dos días que faltan y poder compartir más experiencias y tiempo, pero no va a ser posible. Muchas gracias por estar allí, que disfrutéis mucho y os lo paséis genial.

Muchas gracias a Ethel, Bea & co. por darme la oportunidad de vivir estas experiencias.

Hasta la próxima!

TechEd ’07 – MSP Day

Esta semana, como muchos sabéis, se está celebrando el TechEd en Barcelona. Como MSP tenía la oportunidad de asistir pero no se puede tener todo, así que me voy a tener que aguantar y disfrutar solo de los feedbacks que me dan mis compañeros, algunos vídeos y el material que nos pasarán.

Aunque ayer por la tarde se realizó el evento destinado a los MSP’s que no me perdí. Cogí mi cochecito y me acerqué a ver a mis amigos de los clubes de España y a los compañeros MSP que nunca había conocido.

TechEd07_06

Fue genial; ver a tantos estudiantes motivados con las mismas aficiones e inquietudes que yo y ver la cantidad de ‘monstruos de la tecnología’ que había impresiona.

Lo primero a lo que fui fue a las charlas dirigidas para los MSP’s sobre la comunidad, sobre los MSP’s y los diferentes programas y actividades a los que podemos acceder, sobre el programa de los MVP’s… aibá la hostia!!, hay un chico de 18 años que ya es MVP!!!. Como algunos sabéis no soy muy bueno con los nombres, vaya que tengo memoria de Dori para estos, por eso no pongo nombres. :P. También hubo un MVP que con cinco minutos de presentación le sobró tiempo para dejarnos impresionados… (Comentario de Ethel: «Y este que se ha comido hoy para estar así!»), fue im-pre-sionante!.

TechEd07_25

Bueno, luego nos fuimos todos a la bolera del centro comercial Diagonal Mar a «conocernos mejor» y, en mi caso, a «improve» mi inglés – para quien no lo sepa, yo hablo «catanglish», es algo más difícil de entender que el «spanglish». Fue divertido e instructivo hacerme entender con mis nuevos amigos.

Al final en mi equipo éramos todos españoles menos uno, Bojan Vrhovnik de Eslovenia.

TechEd07_10

La partida empezó… fatal: no me quise cambiar los zapatos y me acerqué demasiado a la pista en mi primer tiro, así que la combinación de la cera de la pista + zapato incorrecto + prisas y risas acabó con una buena caida de culo en la pista, un espectáculo vamos. Al final quedé segundo de mi grupo y el chico de Eslovenia primero. Pero nos divertimos un montón. Una experiencia genial.

Casi al final de la partida vino Brínquez un ratito. Como veis, se «nota» que ha llegado… parece un gran pingüino entre todos nosotros – Un BsoT Jordi!.

TechEd07_12

Luego nos pillaron a Carlos y a mí para una entrevista para Channel 8… en inglés!, jaja, quien me mandaba a mi meterme en estos líos. Pero estuvo bien, a ver si sale un día de estos publicada y os enlazo el video; seguramente estará en la sección de tomas falsas o humor, ya que dudo que un inglés pueda llegar a entender algo de lo que dije, imaginaos: «catanglish» + nervios de cámara… Pero que creo que, gracias a la aportación de Carlos, el vídeo no va a ser del todo malo.

Unas partidas al futbolín con Ethel, que nos dio a conocer su lado más competitivo, unas cervecitas, conocimos a los nuevos «donetes» de la Universidad de la Salle y luego decidimos ir a tomar algo hacia el centro de Barcelona.






TechEd07_18 TechEd07_30

Nos fuimos a un local pequeñito de Plaza Reial a tomar algo donde nos encontramos con una de las personas que más me han motivado a aprender .NET y a meterme en este mundo, mi amigo David Salgado!, cada vez está más fuerte el cabroncete.






TechEd07_19 TechEd07_21

Bueno… unas risas, unas copas y para casa, que hoy, como veis, estoy «trabajando», ya que no me queda más remedio.

Todas las fotos que hice ayer las podéis ver en mi galería de flickr.

Hoy toca ir a tomar algo con todos los invitados al TechEd de España, así que, si no pasa nada, mañana o pasado os comento que tal y cuelgo más fotos.

MVP’s, MSP’s, estudiantes y otros animales

Hace dos semanas pude asistir al TTT en las oficinas de Microsoft una vez más. Volver a ver a mis amigos de los clubes, estudiantes y ex-estudiantes que, como yo, han aportado y aportan mucho valor a nuestra comunidad. He podido ver a viejos amigos como Ricardo Varela, que fue mi mentor en este mundillo y el que me abrió las puertas al mundo de .NET. A personajes geniales como Chema Alonso. A maestros como David Salgado o David Carmona que siempre consiguen que alucines con sus presentaciones.

foto1

Hace ya algo más de tres años que fundé mi club en Barcelona junto con mis compañeros de universidad. Muchos ya han acabado y son ingenieros. Otros aun estamos con los codos pelaos. Y también ha aparecido gente nueva, con nuevas energías, ideas e ilusiones que nos alegran y animan a continuar.

Parece mentira como pasa el tiempo. Como me vi sentado en la reunión de clubes que hicimos durante el TTT…, vi a un montón de gente nueva con ilusiones y ideas nuevas. Me gusta ver como estos nuevos chicos tienen la misma ilusión que tenía yo al empezar, las mismas inquietudes. Ojalá hubiera más gente así.

La verdad es que me lo pasé genial en Madrid. Me convertí, como siempre que voy a eventos así, en esponja: absorbiendo todos los conocimientos que pude y conociendo a un montón de personas geniales.

Las presentaciones fueron bastante bien aunque, como siempre, faltó mucho tiempo. Como no, las que más me gustaron fueron la de Silverlight y AJAX de Carmona y la de seguridad de Chema y Ricardo. También nos metieron por el medio una presentación de la XBox 360, con los nuevos juegos de Halo 3 y PGR4.

Lo mejor de las presentaciones… la gente con la que me codeé y las comenté. 🙂

La cena no estuvo nada mal. Un buen lugar para conocer a los nuevos y hablar de fricadas. Además, como todo el mundo sabe, con dos buenas cervezas, la mente está mucho más clara y se entienden mejor las cosas. Eso si, luego nos dicen que si somos una secta o algo parecido, aunque después de ver el espectáculo de los MVP’s…

foto2

… sin comentarios.

Por la noche… bueno, los mejores nos fuimos de fiesta… jeje!!

Al día siguiente hicimos un Paintball. Formamos equipos de 8 y nos dispusimos a darnos caña. Yo formé parte del equipo ‘Vista’, quedamos segundos ya que la final la perdimos contra ‘XP’… esperad a que tengamos el Service Pack….

foto3

Bueno… así estamos de mal.

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.