martes, 1 de julio de 2014

Obteniendo output parameters de procedimientos PLSQL en PostgreSQL con .NET

En .NET existen dos maneras sencillas de ejecutar una procedimiento PLpgSQL y obtener la información como resultado de su ejecución.

La primera de ellas consiste en utilizar parámetros de salida (output parameters) como argumentos del procedimiento.

Para mostrarles esta manera, en el siguiente código declaro cuatro argumentos para el procedimiento, dos de ellos como parámetros de entrada y dos de ellos como parámetros de salida.

Similar a las funciones o los métodos en otros lenguajes de programación, los procedimientos PlpgSQL son subprogramas que pueden recibir parámetros de entrada (input), salida (output) y ejecutar otros procedimientos, ademas de contar con expresiones para el control del flujo.

Los procedimientos aceptan input parameters (parámetros de entrada), que se utilizan dentro del store procedure como variables locales. También puedes especificar output parameters (parámetros de salida) que permiten que un procedimiento regrese uno o más valores escalares a la rutina que llamo ese procedimiento. Cuando se ejecuta el procedimiento los valores de los parámetros de salida son almacenados en memoria.

Para demostrar su funcionalidad, creo una tabla llamada Products con el siguiente script.

Inserto unos registros de prueba y los ordeno por la columna id de forma descendente de manera que pueda saber cual es el máximo id que me regresá el procedimiento.

Ahora ejecuto el procedimiento, poniéndole valores a sus parámetros. Al terminar su ejecución el procedimiento devolverá el máximo id y la fecha en la que se inserto el registro, estos valores corresponden a sus dos parámetros output.

SELECT usp_insertproduct('M01-04-943419', 'Mesh 3in x 6in');

El código en C# para ejecutar este procedimiento y obtener sus output parameters, quedaría algo así.

La clase principal para ejecutar este código es la siguiente:

Al ejecutar este programa tecleamos el código y el nombre del producto.

Al terminar su ejecución, nos mostrará los valores de los parámetros de salida.

En el arreglo de parámetros definimos los de entrada y de salida. De manera predeterminada todos los parámetros son de entrada excepto los que explícitamente se asignan como salida:

            parameters[2] = new NpgsqlParameter("p_id", p_id);
            parameters[3] = new NpgsqlParameter("p_created",p_created);
            parameters[2].Direction = ParameterDirection.Output;
            parameters[3].Direction = ParameterDirection.Output;

Agregamos los parámetros y ejecutarmos el procedimiento

            cmd.Parameters.AddRange(parameters);
            cmd.ExecuteNonQuery();

Después asignamos los parámetros a variables en C#, para su manipulación en el programa.

       p_id = Convert.ToInt32(cmd.Parameters["p_id"].Value);
       p_created = Convert.ToDateTime(cmd.Parameters["p_created"].Value);

Los output parameters son considerados la mejor opción para regresar valores escalares.

viernes, 23 de mayo de 2014

Utilizando Self-hosting Windows Communication Foundation y PostgreSQL.

Windows Communication Foundation es la parte del .NET Framework que define un modelo de programación común y una API unificada basada en estándares industriales de comunicación para construir aplicaciones distribuidas interoperables que se comunican entre sí mediante el intercambio de mensajes y así cubrir la mayor parte de los requerimientos en computación distribuida.

WCF incluye todas las tecnologías de comunicación abiertas como XML, SOAP y JSON y propietarias de Microsoft como MSMQ, COM+ y .NET Remoting.

Aunque en teoría se pueden construir servicios sin WCF, en la práctica utilizar WCF para construir servicios es mucho más práctico que hacerlo desde cero (start from scratch).

En resumen WCF consiste de un número de bibliotecas .NET que se utilizan para aplicaciones orientadas a servicios.

Antes de empezar con el ejemplo es importante tener claras algunas definiciones que son necesarias para entender el funcionamiento de WCF:

  1. Endpoint: En los sistemas orientados a servicios, es el lugar en donde los mensajes van y vienen cada endpoint requiere de tres elementos: Address, Binding y Contract. Pueden ser especificados de forma imperativa o declarativa.
  2. Address: En donde el servicio se encuentra disponible. Es un URL que se puede formar de 4 partes: Schema, machine, port y path.
  3. Binding: Como se puede acceder al servicio.
  4. Contract: Que operaciones ofrece el servicio.
  5. Servicios: Son procesos independientes definidos por su interfaz.
  6. Componentes: son los bloques de construcción a nivel plataforma tales como las bibliotecas (.dll) que se ensamblan en tiempo de diseño o en tiempo de ejecución.
  7. Objetos: Son los bloques de construcción del nivel más bajo que se encuentran dentro de los componentes y los servicios.

Hay que tener en cuenta que no importa que protocolo se use para el intercambio de mensajes desde las aplicaciones hasta el servicio, WCF siempre representará el mensaje como un objeto Message.

En resumen los servicios WCF son solo objetos que exponen una serie de operaciones que las aplicaciones clientes necesitan invocar. Cuando se construye un servicio se describen estas operaciones mediante un contrato (una interfaz) que debe tener una implementación (clase).

Para que el servicio esté disponible a los clientes se debe de instalar en un entorno de ejecución que hospede el servicio. Hay varias formas en las que puedes hospedar el servicio, puedes usar Internet Information Services (IIS), Windows Activation Services (WAS) y self-host managed applications como una consola de windows o un servicio de Windows.

En el siguiente ejemplo mostraré como utilizar los plantillas de Visual Studio para construir una aplicación con la técnica de self-hosting en la cual el desarrollador es responsable de implementar y administrar el ciclo de vida del proceso de alojamiento.

Este ejemplo utiliza un servicio WCF que realiza la búsqueda del nombre de un producto en una tabla de productos dentro base de datos postgreSQL.

Aquí el script de la tabla de productos

1-.Abrimos Visual Studio y en el explorador de soluciones agregamos un proyecto del tipo WCF Service Library.

2-. Nombramos este proyecto como: Test.WCF.Services

3-.A este proyecto le agregamos dos archivos: la interfaz IProductsContract y la clase ProductsImplementation.

Aquí el código de IProductsContract

Aquí el código de ProductsImplementation.

En este código utilizamos unos ensamblados llamados Tests.WCF.Services.Objects y Tests.WCF.Services.Data que contienen el código de datos y un DTO, este código lo omito en la explicación para centrarme en la construcción del WCF, sin embargo el proyecto completo lo incluyo en el código fuente de la solución.

4-. Agregamos un proyecto del tipo aplicación de consola a nuestra solución, con el nombre: ConsoleHostingService

5-. Tecleamos el siguiente código en la clase Program:

Este código muestra la construcción de un Self-Hosted Service , en la línea siguiente creo el Uniform Resource identifier representado por la clase URI.

 Uri baseAddress = new Uri("http://localhost:8004/");

Después asigno el tipo de objeto de la implementación a una variable.

 Type service = typeof(Tests.WCF.Services.ProductsImplementation);

Ahora hago una instancia de la clase ServiceHost que es la clase que proporciona WCF para alojar endpoints de WCF en las aplicaciones .NET, una vez teniendo la instancia hay que decirle el tipo del servicio que debe inicializarse para recibir las peticiones así como establecerle el endpoint para que los cliente sepan donde enviar las peticiones.

ServiceHost host = new ServiceHost(service, baseAddress);

Bien ahora hay que invocar el método host.Open(); para cargar el runtime de WCF y empezar a escuchar las peticiones.

using (host)
      {
        Type contract = typeof(Tests.WCF.Services.IProductsContract);
        host.AddServiceEndpoint(contract, new WSHttpBinding(), "Products");
        host.Open();
        Console.WriteLine("Products service running.Press  to quit.");
        Console.ReadLine();
        host.Close();
     }

La flexibilidad de self-hosting aplica cuando las aplicaciones son (in-proc), esto es que las aplicaciones están en el mismo proceso que el cliente.

Aquí el código del programa cliente.

Es importante que antes de ejecutar el programa cliente, se ejecute primero el programa que contiene el proceso ServiceHost esto lo podemos configurar en VS, en el submenú Set Startup Projects debajo del menú Debug en VS.

Aquí la búsqueda en la tabla utilizando el Query Management de PgAdmin

Ahora ejecutamos la misma búsqueda, solo que remotamente con la solución de VS, primero ejecutamos el proyecto ConsoleHostingService.

Inmediatamente después ejecutamos el proyecto ConsoleHostingClient.

Iniciamos con la búsqueda de productos, tecleamos la coincidencia a buscar y pulsamos ENTER, esto hará que se mande la petición al servicio WCF.

lunes, 19 de mayo de 2014

Deshabilitar el modo autocommit de transacciones en SQL Server

Hay 3 modos básicos de transacciones con los cuales trabaja SQL Server. Estos modos son:

  1. AutoCommit: Cada comando SQL/T-SQL tiene su propio espacio de transacción y automáticamente se hace la confirmación (COMMIT) cuando el comando termina.
  2. Explicit: En este modo se indica el inicio y el final de una transacción de manera programática. Utilizando los comandos: BEGIN TRAN , COMMIT TRAN y ROLLBACK TRAN. Este modo es comúnmente utilizado en store procedures o scripts.
  3. Implicit: En este modo, cuando emites ciertos comandos SQL Server automáticamente empieza una transacción. Entonces debes de confirmar o deshacer la transacción explícitamente utilizando los comandos COMMIT/ROLLBACK.

Autocommit es el modo predeterminado en SQL Server para las transacciones. Aunque este modo puede ser cómodo para el programador ya que no se tienen que escribir los comandos implicitos para abrir y cerrar una transacción, esto resulta peligro en ciertas ocasiones sobre todo si los datos con los que estamos trabajando se encuentran en un ambiente productivo.

Una manera de disminuir ese riego es deshabilitar el modo autocommit.

Esto se logra habilitando el modo de transacciones implícitas con el comando:

SET IMPLICIT TRANSACTIONS ON

Mostraré un ejemplo, habilitaré el modo de transacciones implícitas en SQL Server 2012 y agregaré un registro nuevo a la tabla HumanResources.Department de la base de datos AdventureWorks2012.
Ejecuto los siguientes comandos para agregar un registro.

SET IMPLICIT TRANSACTIONS ON
go
Insert into HumanResources.Deparment(Name,GroupName,ModifiedDate)
values('Software Developer','Research and Development',getdate())
go

Consulto la tabla para ver que se agregó el registro a la tabla.

SELECT * FROM HumanResources.Department ORDER BY DepartmentID DESC

Hasta este punto, si ejecuto ROLLBACK puedo deshacer la modificación a la tabla, si vuelvo a consultar la tabla los datos son los mismos que estaban almacenados antes del INSERT.

ROLLBACK

Vuelvo a ejecutar el INSERT si ahora deseo que la modificación sea permanente, tengo que ejecutar un COMMIT.

Vuelvo a consultar los datos de la tabla y comprobamos que el registro se guardo de forma permanente.

En este modo, si hay comandos que no han sido confirmados (COMMIT) o rechazados (ROLLBACK), SQL Management nos avisa al momento de cerrar la aplicación.

miércoles, 7 de mayo de 2014

Utilizando Message Queue con C# en .NET Parte II

Continuando con el tema de Microsoft Message Queue, mostraré como recibir un mensaje almacenado en la cola de espera. En la primera parte de este tutorial enviamos un mensaje a una cola de espera por lo que ahora hare un programa que utilice el método Receive() de la clase MessageQueueHelper que puse de ejemplo en la primera parte de este tutorial para obtener ese mensaje y mostrarlo.

Hay que recordar que MSMQ trabaja con un principio FIFO (First In, First Out) lo que significa que el primer mensaje en la cola es el mensaje que recuperarás utilizando los métodos Receive() o Peek() de la clase MessageQueue.

Aquí el código fuente del programa:

Al ejecutar el programa se verá la siguiente salida:

Ahora voy a explicar cómo funcionan los métodos de la clase MessageQueueHelper. El método CreateQueue() recibe tres parámetros que indican las siguientes propiedades:

  1. Path: Indica la ubicación de la cola de espera.
  2. Transactional: Especifica si la cola puede aceptar transacciones de mensajes.
  3. Label: El título que describe de la cola de espera.
Para el valor del Path, el “.” Indica que estamos creando la cola de espera en la maquina local y utilizamos el prefijo Private$ para indicar que es una cola de espera privada, si omites este prefijo indica que la cola de mensajes es pública.

 queue = MessageQueue.Create(queuePath,isTransactional);
 queue.Label = label;

En el método SendMessage() utilizamos le asignamos la clase XmlMessageFormatter para dar un tipo de formato al mensaje. Los MessageFormatters implementan la interfaz IMessageFormatter, la cual puede ser aplicada a un mensaje o a toda la cola de espera. Es indispensable que utilices el mismo MessageFormatter tanto para enviar o para recibir el mensaje. Aquí una lista de los Formatters:

  1. ActiveXMessageFormatter: Se utiliza para trabajar con componentes COM.
  2. BinaryMessageFormatter: Utilizado para serializar o deserializar un mensaje en un formato binario.
  3. XMLMessageFormatter: El formatter predeterminado, serializa y deserializa en formato XML.
Aquí está el código donde se utiliza:

 queue = new MessageQueue(queuePath);
 queue.Formatter = new XmlMessageFormatter();

Por último en el método GetMessage() igual que para enviar el mensaje utilizo una clase MessageFormatter para deserializar el mensaje, también utilizo el método Receive() de la clase MessageQueue para obtener el primer mensaje de la cola de forma síncrona, como argumento recibe un TimeSpan que representa el intervalo de tiempo de espera para recibir el mensaje, este argumento es importante o de lo contario si no se recibe el programa se bloqueara hasta que no haya recibido el mensaje.

 queue = new MessageQueue(queuePath);
 queue.Formatter = new XmlMessageFormatter(new Type[]{
 Type.GetType("System.String")
 });
 queueMessage = queue.Receive(new TimeSpan(0,0,2));
 message = queueMessage.Body.ToString();

lunes, 5 de mayo de 2014

Utilizando Message Queue con C# en .NET

Microsoft Message Queue (MSMQ) es un servicio de Windows que permite la comunicación interproceso entre diferentes computadoras de forma asíncrona, entender cómo funciona puede resultar útil en cualquier momento que los clientes y los servidores no tengan una conexión disponible, ya que los mensajes entre ellos son encolados para su transmisión hasta que ambos estén conectados.

El funcionamiento básico de MSMQ es parecido al de una mensajería entre aplicaciones de software, una aplicación prepara un mensaje y lo escribe hacia una línea de espera o cola de mensajes MSMQ. En este contexto una cola de mensajes es un área de almacenamiento temporal que guarda los mensajes hasta que sean transmitidos. El administrador de colas actúa como un intermediario que transmite el mensaje desde su aplicación origen hasta su destino. El propósito de la cola de mensajes MSMQ es el enrutamiento y garantizar la entrega del mensaje, en caso de que el destinatario del mensaje no esté disponible la cola de mensajes mantendrá el mensaje hasta que pueda ser entregado con éxito.

Este mecanismo de transporte proporciona los siguientes beneficios:

  1. Robustez: Los mensajes son almacenados en la línea de espera hasta que son procesados. Si se cae la conexión entre un cliente y un server los mensajes se guardan hasta que una conexión este presente.
  2. Mensajería basada en prioridad:Los mensajes más importantes son los primeros en ser recibidos, después los mensajes que le siguen y así sucesivamente, hasta que los últimos son los de prioridad más baja.
  3. Garantía de entrega:Cuando se llaman los mensajes a través de la red, MSMQ utiliza una transacción para entregar los mensajes, si se lee un mensaje de la línea de espera y ocurre un error se hace un rollback y el mensaje regresa a la línea de espera.
  4. Seguridad: La tecnología de MSMQ utiliza la seguridad de Windows para proporcionar el control de acceso, la auditoría, encriptación y autenticación de mensajes, de hecho el ensamblado System.Messaging es un wrapper para una API nativa.

Hay tres tipos de líneas de espera en MSMQ:

  1. Públicas: están registradas en el Active Directory y permiten a todas las maquinas enviar y recibir mensajes a través de la red.
  2. Privadas: solo se crean en la máquina local y pueden ser únicamente accedidas desde otra máquina si se conocen los nombres exactos de la máquina y de la línea de espera.
  3. Sistema: son principalmente utilizadas para propósitos administrativos o para aplicaciones que necesitan una línea de espera que guarde copias de los mensajes que son procesados, por ejemplo las aplicaciones de auditoría.

Como ejemplo de la utilización de MS Message Queue con C#, hice el siguiente programa que consta de dos clases: MessageQueueHelper y TestMessageQueue, la primera clase tiene los métodos para crear la cola (CreateQueue), enviar mensaje (SendMessage) y recibir mensaje (GetMessage), la segunda clase es un programa que muestra el uso de los métodos (CreateQueue) y (SendMessage).

Aquí el código de la clase MessageQueueHelper

using System;
using System.Messaging;

namespace Samples{
 public class MessageQueueHelper{
static MessageQueue queue = null;
public static Guid CreateQueue(string queuePath,
  bool isTransactional,
  string label)
{
 if(MessageQueue.Exists(queuePath))
  queue = new MessageQueue(queuePath);
 else
 {
  queue = MessageQueue.Create(queuePath,isTransactional);
  queue.Label = label;
 }
 return queue.Id;

}

public static void SendMessage(string queuePath,
  string message,string subject)
{
 message += "\nCreated on " + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss");
 MessageQueueTransaction trans = null;
 if(MessageQueue.Exists(queuePath))
 {
  queue = new MessageQueue(queuePath);
  queue.Formatter = new XmlMessageFormatter();
  if(queue.Transactional)
  {
  trans = new MessageQueueTransaction();
  trans.Begin();
  }
  queue.Send(message,subject);

  if(queue.Transactional)
   trans.Commit();
  Console.WriteLine("Message sent.");
 }else
  Console.WriteLine("The queue doesn't exist");
}

public static string GetMessage(string queuePath)
{
 string message = null;
 Message queueMessage = null;
 if(MessageQueue.Exists(queuePath))
 {
  queue = new MessageQueue(queuePath);
  queue.Formatter = new XmlMessageFormatter(new Type[]{
    Type.GetType("System.String")
    });
  queueMessage = queue.Receive(new TimeSpan(0,0,2));
  message = queueMessage.Body.ToString();
 }
 else
  message = "That queue doesn't exist";
 return message;
}

 }
}

Bien ahora el código de la clase TestMessageQueue que prueba los métodos de la clase anterior.

using System;

namespace Samples{
public class TestMessageQueue{
public static void Main(string[] args){
bool isTransactional = false;
Console.WriteLine("\nTest MSMQ\n");
try
{
 Console.Write("\nEnter path > ");
 string path = Console.ReadLine();
 Console.Write("\nIs transactional? (1=true/0=false) > ");
 string sTransactional = Console.ReadLine();
 if(sTransactional.Equals("1"))
  isTransactional = true;
 Console.Write("\nEnter a label > ");
 string label = Console.ReadLine();
 Guid guid = MessageQueueHelper.CreateQueue(path,isTransactional,label);
 Console.WriteLine("\nMessageQueue created with guid {0}\n",guid);
 Console.Write("\nMessage subject > ");
 string subject = Console.ReadLine();
 Console.Write("\nMessage to send > ");
 string message = Console.ReadLine();
 Console.WriteLine();
 MessageQueueHelper.SendMessage(path,message,subject);
}
catch(Exception ex){
 Console.WriteLine(ex.Message);
}
}

}
}

Al ejecutar el programa solicitará los siguientes parámetros:

  • Path: Es el nombre de la cola de espera.
  • Is transactional: 1 si la cola es transaccional, 0 si no lo es.
  • Label: Identificador de la cola.

Al crearse la cola de mensajes el programa regresará el identificador: 00000000−0000−0000−0000−000000000000 por tratarse de una cola de mensajes privadas (private MSMQ).

Ahora el programa solicitará el título y el cuerpo del mensaje, después lo enviará hacia la cola de mensajes creada.

Para ver la cola de mensajes creada con el programa y el mensaje enviado, ejecutamos Computer Management de Windows que se encuentra en la ruta Control Panel\System and Security\Administrative Tools y debajo del nodo de Services and Applications veremos el nodo Private Queues debajo del nodo Message Queuing donde se encuentra la línea de espera creada con el programa de ejemplo.

Debajo de la cola de espera myfirstqueue, se encuentra la carpeta Queue messages en donde se puede ver la etiqueta del mensaje.

Si hacemos doble click en el mensaje aparecerá una ventana con cuatro pestañas en donde podemos ver las propiedades del mensaje, en la pestaña body podemos ver el contenido del mensaje.

martes, 22 de abril de 2014

Utilizando Extension Methods en .NET con C#

En programación orientada a objetos, la herencia es uno de los mecanismos más eficientes para extender la funcionalidad de los objetos que componen un sistema, este mecanismo permite que un objeto herede atributos y comportamientos de otro objeto reduciendo así la cantidad de código que tendrá que mantenerse una vez desarrollado el sistema.

Sin embargo agregar funcionalidad a un objeto que no fue diseñado para ser heredado puede ser una tarea no muy recomendable, ya que implica cambiar el diseño de ese objeto y entonces afectar todos los objetos que dependan de él y sus operaciones que lleven a cabo dentro del sistema.

En .NET existe una característica llamada extension methods (métodos de extension) que permite agregar funcionalidad (métodos) a un objeto o tipo ya existente en el framework o en los ensamblados sin la necesidad de crear subclases, recompilar o modificar el código fuente. Esta técnica incluso permite agregar métodos a objetos que provengan de clases selladas (sealed) sin violar el principio de encapsulamiento.

Como ejemplo de esta técnica, agregaré un par de métodos a un tipo incorporado dentro del framework .NET, utilizaré la estructura DateTime que representa el tiempo (fecha y la hora).

A esta estructura voy a agregarle dos métodos uno llamado LastDay que regresa el último día del mes de un objeto DateTime y otro método llamado After que regresa true si el objeto DateTime que lo invoca es mayor a la fecha que recibe como parámetro.

Aquí el código de la clase donde agrego los extension methods

Aquí el código con el programa que prueba los métodos anteriores.

Al ejecutar el programa se ve la siguiente salida.

Propiedades de los extension methods

Pueden aplicarse a cualquiera de los siguientes objetos: Clases, Estructuras, Interfaces, Delegados, Genéricos y arreglos. Deben ser declarados dentro de una clase estática, y tener la palabra reservada this antes de su primer parámetro, que es el tipo que el método extiende, los demás parámetros son los argumentos del método. Son públicos por que se llaman desde fuera de la clase donde ellos son declarados. Pueden aplicarse en clases selladas (sealed) y tipos intrínsecos porque automáticamente soportan boxing y unboxing, es decir cuando se aplican a un tipo por valor .NET los envuelve como si se tratara de una objeto por referencia.

miércoles, 9 de abril de 2014

Compresión y descompresión de flujos(streams) en .NET

Una capacidad muy útil en cualquier Framework es la de comprimir y descomprimir archivos o flujos de datos, ejemplos aplicados de esta capacidad los tenemos en las aplicaciones que intercambian datos a través de la red como el caso de comprimir el viewstate en las peticiones y respuestas del servidor al cliente en una aplicación ASP.NET o si necesitamos reducir el tamaño de los objetos JSON que viajan de una aplicación hacia un servicio web y viceversa.

.NET proporciona las clases GzipStream y DeflatStream que implementan el algoritmo de compresión estándar gzip que está libre de patente. De hecho la diferencia entre ambas clases es que la clase GzipStream utiliza la especificación gzip descrita en el RFC 1952 que reduce el tamaño de los datos utilizando un código Lempel-Ziv (LZ77) con un CRC de 32 bits, lo que significa que se puede incluir encabezados (headers) con información extra que puede ser utilizada por cualquier herramienta de descompresión o por el comando gunzip de Linux o UNIX.

En resumen si los datos comprimidos van a hacer descomprimidos por una aplicación .NET lo recomendable es usar la clase DeflatStream, de lo contrario si van a hacer descomprimidos por otra aplicación usar la clase GzipStream.

Para mostrar la utilización de la clase DeflatStream escribí el siguiente programa que ejecuta la acción de comprimir o descomprimir dependiendo de la extensión del archivo que le pasemos como parámetro. (Este código sirve para ambos flujos solo hay que reemplazar DeflatStream por GzipStream)

using System;
using System.IO;
using System.IO.Compression;

namespace GzipGunzip
{
class MainClass
{
public static void Main (string[] args)
{
if (args.Length == 1)
{
 try{
 FileInfo fis = new FileInfo(args[0]);
 Console.WriteLine("Input file {0}",fis.FullName);
 if(string.Compare(fis.Extension,".gz") == 0)
  Uncompress(fis.FullName);
 else
  Compress(fis.FullName);
 }catch(Exception ex){
  Console.WriteLine(ex.Message);
 }
}else
 Console.WriteLine("\nUsage: GzipGunzip filename\n");
}

static void Uncompress (string filename)
{
Console.WriteLine ("Uncompressing .... {0} ", filename);
string destFile = filename.Substring (0, filename.Length - 3);
FileStream inputStream = new FileStream (filename
                                        , FileMode.Open
                                        , FileAccess.Read);
using (FileStream outputStream = new FileStream (destFile
                                         , FileMode.Create
                                         , FileAccess.Write)) {
 using(DeflateStream zipStream = new DeflateStream (inputStream
                                             , CompressionMode.Decompress)){
 int inputByte = zipStream.ReadByte ();
 while (inputByte != -1) {
  outputStream.WriteByte ((byte)inputByte);
  inputByte = zipStream.ReadByte ();
 }
 }
}

Console.WriteLine("output: {0}",destFile);
}
static void Compress (string filename)
{
Console.WriteLine ("Compressing .... {0} ", filename);
string destFile = filename + ".gz";
byte[] buffer = null;
using (FileStream inputStream = new FileStream(filename
                                 ,FileMode.Open
                                        ,FileAccess.Read)) {
 buffer = new byte[inputStream.Length];
 inputStream.Read (buffer, 0, buffer.Length);

}
Console.WriteLine ("bytes read: {0}", buffer.Length);
using (FileStream outputStream = new FileStream(destFile
                                   ,FileMode.Create
                                         ,FileAccess.Write)) {
 using(DeflateStream zipStream = new DeflateStream (outputStream
                              , CompressionMode.Compress)){
 zipStream.Write (buffer, 0, buffer.Length);
 }
}
Console.WriteLine("bytes written: {0}",buffer.Length);
}
}
}

Ahora para probar el código, voy a crear un archivo llamado dummy.zeros con un tamaño de 20 megas para pasárselo como parámetro al programa.

Este archivo lo creo con el comando dd en Linux

dd if=/dev/zero of=dummy.zeros bs=1000 count=20000

Ahora muestro el tamaño del archivo dummy.zeros, siendo de 20Mb.

Ahora comprimo el archivo dummy.zeros con el programa de ejemplo GzipGunzip, esto lo hago con el siguiente comando:

mono GzipGunzip.exe dummy.zeros

Si se ejecuta correctamente este programa debe crear un archivo llamado dummy.zeros.gz, el cual es el archivo comprimido de dummy.zeros. Comparamos el tamaño de ambos archivos.

Ahora voy a descomprimir el archivo dummy.zeros.gz , pero antes de hacerlo voy a renombrar el archivo original dummy.zeros como dummy.zeros.bak para evitar que el archivo original se sobreescriba y así poder comparar todos los archivos. Ejecutamos nuevamente el programa GzipGunzip pasándole como parámetro el nombre del archivo.

mono GzipGunzip.exe dummy.zeros.gz

Bien ahora con todos los archivos, podemos comparar sus tamaños y comprobar que las clases de compresión de .NET funcionaron.

Para diseñar programas que utilicen las clases de compresión y descompresión de archivos en C#, el primer paso es crear los flujos de entrada y de salida.

FileStream inputStream = new FileStream (filename , FileMode.Open, FileAccess.Read);
FileStream outputStream = new FileStream (destFile , FileMode.Create, FileAccess.Write))

En el caso de la compresión el flujo de compresión (DeflateStream) debe de envolver al flujo de salida, porque la entrada es un archivo sin comprimir que tendrá como destino un archivo comprimido.

DeflateStream zipStream = new DeflateStream (outputStream, CompressionMode.Compress)

Para la descompresión el flujo de compresión (DeflateStream) deberá de envolver al flujo de entrada ya que es un archivo comprimido que tendrá como destino un archivo sin comprimir.

DeflateStream zipStream = new DeflateStream (inputStream, CompressionMode.Decompress)

Hay que recordar que uno de los factores que degradan el desempeño de las aplicaciones ASP.NET es cuando existe un cuello de botella en el ancho de banda y no en la utilización del procesador, en el caso de la compresión de datos usada junto con el protocolo HTTP se recomienda para texto: JSON, XML, HTML, etc. Esta compresión no es útil para archivos de imágenes, los cuales ya se encuentran comprimidos.