sábado, 29 de agosto de 2015

Entendiendo el paso de variables a métodos en C# con ref, params y out.

En C# existen dos formas de pasar variables como parámetros a un método: por valor (Passing by value) que es la forma predeterminada y por referencia (Passing by reference, Passing by address). La definición del parámetro de un método consiste en el tipo de dato, el nombre del parámetro y si son más de uno se separan por coma como en los siguientes códigos:

  int method(string p)
  int method(string p, int q, bool r, float t);
  

Paso por valor (Passing by value)

Esta es la forma predeterminada en la que los métodos reciben los parámetros. Esto quiere decir que cuando un parámetro se recibe dentro del método, se crea una copia temporal de los datos, esta copia temporal actúa como una variable dentro del método con la que se realizan operaciones, sin afectar la variable original y una vez que el método termina su ejecución, la copia temporal es destruida, por lo tanto los cambios en el parámetro no afectan el valor de la variable original. Estos conceptos se observan mejor con el siguiente programa:

Este programa intercambia el valor de unos enteros en donde se inicialmente se les asigna un valor, esos enteros después pasan como parámetros, se ejecuta el método y las variables conservan su valor inicial. Aquí muestro la siguiente salida:

Paso por referencia (Passing by reference)

Hay 3 modificadores de parámetros en C# que sirven para pasar por referencia una variable a un método y estos son:

  1. ref
  2. out
  3. params

Utilizando ref

Cuando se pasan las variables como parámetros por referencia, el parámetro es en realidad una referencia a la dirección de memoria de la variable que se recibe, por lo que los cambios hechos al parámetro afectan a la variable original. Para pasar un parámetro por referencia, se antepone a la definición del parámetro la palabra reservada ref. Para ilustrar este concepto utilizo el programa del ejemplo anterior que intercambia dos valores enteros, pero con la modificación de pasar las variables por referencia (con ref) al método no por valor. Aquí el código:

Al ejecutar este programa se produce el intercambio de variables que realiza el método swap, porque ya no trabaja con copias locales sino con las variables globales.

Aquí el método swap con el modificador ref usado en la declaración de variables del método.

static void Swap(ref int number1, ref int number2)
    {
    int temp = number1;
    number1 = number2;
    number2 = temp;
    }

También se debe utilizar el modificador con sus argumentos al invocarlo.

Swap(ref a, ref b);

Utilizando params

En los ejemplos anteriores el número de parámetros está fijo en la declaración del método. Sin embargo, C# soporta el uso de un arreglo de parámetros (parameters array), la palabra reservada params permite pasar cualquier número variable de parámetros hacia un método sin necesidad de declararlos como un arreglo. Esta instrucción solo se requiere en la declaración del método. Con el siguiente programa muestro el uso de params para calcular el promedio de un arreglo de números y para armar una cadena.

Este programa produce la siguiente salida.

Utilizando output parameters

Un método de forma predeterminada solo puede devolver un valor mediante la instrucción return, al menos que se utilicen los output parameters (parámetros de salida), con esta característica pueden devolver tantos valores de salida como parámetros de salida tengan, no es necesario asignar valores a esos parámetros afuera del método sino se debe asignar el valor dentro del método que los va a utilizar o de lo contrario el compilador marcara un error.

Para utilizar los output parameters se debe anteponer la palabra reservada: out en la declaración de cada parámetro. Como se muestra en el siguiente código:


static void SetEnviromentProperties(out string currentDirectory,
    out string machineName,
    out string osVersion,
    out string userName,
    out int exitCode)

Bien con el siguiente programa mostraré el uso de esta característica.

Al ejecutar el programa, se vera la siguiente salida:

Al ejecutarse el método SetEnviromentProperties sus correspondientes argumentos también deben tener la palabra reservada out, para que dentro del método se les asigne el valor correspondiente.


static void SetEnviromentProperties(out string currentDirectory,
    out string machineName,
    out string osVersion,
    out string userName,
    out int exitCode)

Finalmente hay que recordar que un método que define output parameters DEBE asignar un valor a los parámetros out antes de que el método termine de ejecutarse, de lo contrario el compilador marcara un error.

sábado, 22 de agosto de 2015

Instalando Oracle JRE y JDK en Open SuSe 13.2

Por cuestiones de dependencias no pude instalar los archivos jdk-8u45-linux-x64.rpm y jre-8u45-linux-x64.rpm respectivamente, aunque yo no trabajo con Java, en Linux es una dependencia importante para OpenOffice y DbVisualizer así como para muchos otros programas, por lo sino es posible hacerlo con los RPMS entonces recomiendo una instalación manual, siguiendo los siguientes pasos en OpenSuse 13.2:

1) Se descargan del sitio de Oracle los archivos jre-8u45-linux-x64.tar.gz y jdk-8u45-linux-x64.tar.gz en vez de los rpm.
2) Debajo del directorio /usr se crea el directorio java.(como root)

# mkdir /usr/java
3) Se copian/mueven los archivos descargados al directorio creado anteriormente.

4) Se descomprimen los archivos, de preferencia en el siguiente orden:

  1. tar -zxvf jre-8u45-linux-x64.tar.gz
  2. tar -zxvf jdk-8u45-linux-x64.tar.gz

Al descomprimirse se crea el árbol de archivos del jre y jdk respectivamente.

Bien, ahora sigue un paso crucial que es agregar los recién descomprimidos jre y jdk de Oracle al directorio

/etc/alternatives
. En mí caso desinstalé todas las versiones de openjdk que tenia en OpenSuse y de manera predeterminada me asigno el siguiente:
/usr/lib64/jvm/jre-1.5.0-gcj/bin/java 
como la máquina virtual. Esto porque al parecer existe software que necesita por fuerza una máquina virtual.

5) Así como ya existe una máquina virtual predeterminada y lo que quiero es usar la máquina virtual de Oracle, ejecuto los siguientes comandos para registrar la máquina virtual en el directorio

/etc/alternatives
.

update-alternatives --install /usr/bin/java java /usr/java/jre1.8.0_45/bin/java 1 
update-alternatives --install /usr/bin/java java /usr/java/jdk1.8.0_45/bin/java 0

6) Ahora ejecuto el comando:

update-alternatives --config java

Si todo sale correctamente OpenSuse mostrará la siguiente pantalla:


En este punto seleccionamos la máquina virtual que necesitemos utilizar, en mí caso seleccioné la opción 2 que es el JRE de Oracle.

Si todo sale bien al ejecutar el comando:

java -version

Mostrará el siguiente resultado, mostrando la versión correspondiente a la JVM de Oracle.


Como último detalle hay que agregar al entorno la variable JAVA_HOME, esto en mí caso lo hice agregando las siguientes líneas al archivo .profile

JAVA_HOME = /usr/java/jdk1.8.0_45
export JAVA_HOME

En mí caso lo hice apuntando al jdk en vez del jre, esto lo hice para instalar netbeans posteriormente.

Más información en los siguientes enlaces:

lunes, 17 de agosto de 2015

Entendiendo DataContract y DataMember de WCF con GTK# y MonoDevelop

Un contrato se define como: “un acuerdo entre partes que se debe de cumplir de manera obligada por cada una de ellas”, los contratos deben ser claros, definidos y sin ambigüedad para no dar lugar a una mala interpretación.

Windows Communication Foundation WCF utiliza este mismo concepto para definir un acuerdo entre clientes y servicios de un ambiente SOAP, en este contexto el contrato WCF define:

  • Las operaciones soportadas por el servicio.
  • Los parámetros y los tipos de datos que regresan las operaciones.
  • La estructura de los tipos de datos complejos que se pasan.
  • Errores que pueden ocurrir al ejecutar una operación.

Hay que recordar que los ambientes SOA son heterogéneos, por eso es recomendable que el contrato este separado de la implementación y que esa implementaciòn evite tipos específicos de su tecnología ya que esto podría causar problemas de portabilidad para comunicarse con un cliente que este implementado con una tecnología diferente.

Los contratos en el contexto SOA proporcionan todo el metadata necesario para comunicarse con el servicio, el metadata describe: tipos de datos, operaciones, patrones de intercambio de mensajes y el protocolo de transporte que se utiliza.

WCF define tres principales tipos de contrato:

  • Service contract: Define las operaciones que el servicio tendrá disponibles para que los clientes puedan invocar mediante una petición request.
  • Data contract: Define la estructura de los datos que serán incluidos en el intercambio de los mensajes que van y vienen del servicio al cliente y viceversa.
  • Message contract: Habilita el control de los headers (encabezados) que utilizan los mensajes y como son utilizados por el servicio.

WCF utiliza el protocolo SOAP (Simple Object Access Protocol) como el formato de mensajería para las operaciones, cada operación desde el service contract (contrato de servicio) se vuelve un elemento en el cuerpo del mensaje cuando la operación es invocada.

En la definición de la operación se especifican los datos que son intercambiados entre el cliente y el servicio cuando la operación es invocada, como ejemplo la siguiente operación createAuthor para agregar un autor a una base de datos:

            [OperationContract]
        string CreateAuthor (string Firstname,
            string Lastname,
            DateTime Birthdate,
            bool Gender); 
        

  • Los parámetros de entrada en la operación definen los contenidos del cuerpo del mensaje enviado desde el cliente al servicio.
  • El valor de retorno en la operación define los contenidos del cuerpo del mensaje enviado desde el servicio al cliente.

De manera predetermina el runtime de WCF toma la responsabilidad de convertir los tipos simples de .NET a tipos SOAP y viceversa, definiendo los tipos simples como:

  • Primitivos como enteros y cadenas.
  • Tipos compuestos simples (struct) como DateTime
  • Tipos basados en XML como un XmlElement
  • Colecciones
  • Enumeraciones

Sin embargo para tipos mas complejos como las clases o los tipos de datos definidos por el usuario es recomendable que se programe manualmente el mecanismo de serialización/ desealización y esto se logra definiendo un DataContract para ese tipo de dato.

En síntesis, para que un tipo de dato complejo sea serializado por el serializador de WCF se le debe aplicar un atributo DataContract a la definición de la clase y aplicar un atributo DataMember a cada uno de los campos que necesiten ser serializados sean estos un miembro de datos o una propiedad, los campos a los que no se les aplique el atributo DataMember son ignorados por el serializador.

Como ejemplo de los atributos DataContract y DataMember definimos una clase Author que encapsula los parámetros de entrada de una nueva versión de la operación createAuthor.

Ahora la versión de la operación createAuthor

            
        [OperationContract]
        string CreateAuthor (Author a); 
        

Adicionalmente podemos agregarle las siguientes propiedades al atributo DataContract:

  • Name: Define el nombre del tipo que se genera en el metadata. Por default se utiliza el nombre del tipo.
  • NameSpace: Define el namespace utilizado en el esquema. Por default utiliza “http://tempuri.org”

De igual modo podemos agregar las siguientes propiedades al atributo DataMember:

  • Name: Define el nombre que se utilizará en la generación del metadata. Por default es el nombre del campo.
  • IsRequired: Campo requerido, arroja una excepción si este campo no está cuando ocurra la deserialización.
  • EmitDefaultValue: Le dice al serializador que incluya el valor por default del campo cuando ocurra la serialización.
  • Order: Indica la posición del campo en la secuencia de serialización.

Ejemplo de una aplicación GTK# y un servicio WCF

Ahora un ejemplo funcional de una aplicación GTK# que hace uso de un servicio WCF para insertar y obtener registros de una tabla de autores en una base de datos PostgreSQL, aquí esta el esquema de la tabla. (Para más información de una solución WCF en Monodevelop consultar Introducción a WCF con GTK# y MonoDevelop)

A continuación el código del store en PL/SQL para insertar un registro

Bien ahora una vista de la solución en el explorador de soluciones de Monodevelop.

La solución se llama Samples.WCF.AuthorsCatalog y tiene los siguientes proyectos:

  • Samples.WCF.AuthorsCatalog: Este proyecto contiene la entidad Author que es la clase que es serializada por el runtime para intercambiar los datos entre la aplicación GTK# y el servicio WCF.
  • Samples.WCF.AuthorsCatalogService: Este es el proyecto del servicio WCF, contiene la clase AuthorsDataHelper que es la que se comunica con la base de datos, la clase AuhorServiceImplementation que es la implementación del servicio y la interface IAuthorServiceContract que es el contrato del servicio.
  • Samples.WCF.AuthorServiceHost: Este proyecto contiene una aplicación de consola que es el hosting para el servicio WCF.
  • Samples.WCF.GUIAuthorsCatalog: Este proyecto contiene al cliente GTK# que tiene una GUI (Graphical User Interface) que utiliza la clase proxy para comunicarse con el servicio WCF.

El proyecto Samples.WCF.AuthorsCatalog ejemplifica la aplicación de los atributos DataContract y DataMember con el código de la siguiente clase:

Esta clase sirve como argumento de petición y de respuesta en el contrato y por consecuente en la implementación del servicio como muestro en el código fuente del contrato y de la implementación que se encuentran en el proyecto [Samples.WCF.AuthorsCatalogService]. Aquí esta el código fuente del contrato del servicio IAuthorServiceContract:

Ahora el código fuente de la implementación AuthorServiceImplementation:

Para ejecutar la solución primeramente ejecutamos el programa de consola [Samples.WCF.AuthorServiceHost.exe] que activa el proceso que alberga el servicio WCF, el ejecutable se encuentra dentro del directorio “bin” debajo de la ruta Samples.WCF.AuthorsCatalog/Samples.WCF.AuthorServiceHost/bin/Debug. con el siguiente comando:

            $ mono Samples.WCF.AuthorServiceHost.exe
        

Ahora ejecutamos la solución desde MonoDevelop.

Podemos probar la aplicación al agregar un par de autores, como se muestran en la siguientes imágenes, después de ingresar cada autor pulsamos el botón refresh grid

Cada uno de estos eventos se manejan a tráves de la clase proxy que se genera con el siguiente comando aplicándolo a la dll del servicio.

            $svcutil /out:AuthorServiceReference.cs  Samples.WCF.AuthorsCatalogService.dll
        

Conclusión

WCF proporciona facilidades para tomar el control de la serialización de datos. Sin embargo, hay que entender que entre más control manual tomes de tus servicios estos tenderán a ser menos interoperables.