Hola a todos!

Hoy voy a comentar algo bastante curioso con lo que me he topado trabajando con Entity Framework. A modo de preámbulo, os diré que durante la ejecución de una aplicación, cada vez que instanciábamos un objeto de una clase que heredase de ObjectContext se lanzaban una serie de excepciones, cuyo número iba creciendo. Es decir, que simplemente invocando al constructor, sin conectarse aún a ningún sitio, podíamos ver cómo se lanzaban 2 excepciones. A la siguiente invocación del constructor (de la misma clase!) podían lanzarse 3... Es decir, el número de excepciones iba "in crescendo". Y aunque iban aumentando el número de excepciones por invocación de constructor, no había un patrón para poder determinar por qué iba aumentando ese número. O al menos el patrón no era aparente del todo (tranquilos que al final desenmascararemos el misterio).

Por otro lado, para agravar más aún la situación, los ObjectContext tenían una vida cortísima, y se creaban y destruían continuamente (arquitectura empresarial, acceso a datos desde componentes sin estado... bueno, os hacéis a la idea, ¿verdad?), con lo que el número de excepciones lanzadas era... enorme.

Como a estas alturas ya sabréis, en las cadenas de conexión de Entity Framework hay que indicar varios parámetros:

  • El proveedor de acceso a datos a utilizar
  • La cadena de conexión a base de datos (de las de ADO.NET de toda la vida) que usa el proveedor del punto anterior.
  • Dónde están los ficheros de metadatos.

Para crear cadenas de conexión mediante código, se recomienda utilizar la clase EntityConnectionStringBuilder, tal y como se indica aquí.

Bien... Pues la causa de las excepciones era el cómo indicábamos dónde están los metadatos. Los ficheros de metadatos los tenemos dentro de los ensamblados en los que tenemos definidas las entidades. Por lo tanto, la ubicación de los metadatos la estábamos indicando de la siguiente manera:

res://*/MyEntities.csdl|res://*/MyEntities.ssdl|res://*/MyEntities.msl

Que quiere decir: Busca en los recursos del ensamblado "*" (es decir, en todos los ensamblados), un recurso que se llame "MyEntities.csdl" (modelo conceptual), otro que se llame "MyEntities.ssdl" (modelo de almacenamiento) y otro que se llame "MyEntities.msl" (mappings).

En casos normales, la búsqueda de esos metadatos no debería dar ningún problema. La búsqueda de los metadatos (según el MSDN) se lleva a cabo de la siguiente forma:

  1. Busca el recurso indicado en el ensamblado que realiza la llamada
  2. Busca el recurso indicado en los ensamblados referenciados desde el ensamblado del punto anterior
  3. Busca el recurso indicado en los ensamblados que se encuentran en la carpeta de trabajo de la aplicación

Esta información no es incorrecta, pero es incompleta. La búsqueda de los recursos se lleva a cabo utilizando la misma táctica que cuando se debe localizar un ensamblado concreto. Es decir, que el tercer paso sería más bien así:

  • Busco en los ensamblados cargados en el dominio de aplicación
  • Busco en los ensamblados de la carpeta de trabajo actual
  • Busco en los ensamblados de la Global Assembly Cache (GAC).

Esto creo que es así por la excepción que me lanzaban los constructores de las clases derivadas de ObjectContext. La excepción en concreto era la siguiente:

Ocurrió NotSupportedException: No se admite el miembro invocado en un ensamblado dinámico.

Y la pila de llamadas era la siguiente:

mscorlib.dll!System.Reflection.Emit.AssemblyBuilder.GetManifestResourceNames() + 0x39 bytes
System.Data.Entity.dll!System.Data.Metadata.Edm.MetadataArtifactLoaderCompositeResource.GetManifestResourceNamesForAssembly(System.Reflection.Assembly assembly) + 0x24 bytes
System.Data.Entity.dll!System.Data.Metadata.Edm.MetadataArtifactLoaderCompositeResource.AssemblyContainsResource(System.Reflection.Assembly assembly, ref string resourceName = "MyEntities.csdl") + 0x22 bytes
System.Data.Entity.dll!System.Data.Metadata.Edm.MetadataArtifactLoaderCompositeResource.LoadResources(string assemblyName, string resourceName, System.Collections.Generic.ICollection<string> uriRegistry = Count = 0, System.Data.Metadata.Edm.MetadataArtifactAssemblyResolver resolver = {System.Data.Metadata.Edm.DefaultAssemblyResolver}) + 0x91 bytes
System.Data.Entity.dll!System.Data.Metadata.Edm.MetadataArtifactLoaderCompositeResource.CreateResourceLoader(string path, System.Data.Metadata.Edm.MetadataArtifactLoader.ExtensionCheck extensionCheck, string validExtension, System.Collections.Generic.ICollection<string> uriRegistry, System.Data.Metadata.Edm.MetadataArtifactAssemblyResolver resolver) + 0xba bytes
System.Data.Entity.dll!System.Data.Metadata.Edm.MetadataArtifactLoader.Create(string path, System.Data.Metadata.Edm.MetadataArtifactLoader.ExtensionCheck extensionCheck, string validExtension, System.Collections.Generic.ICollection<string> uriRegistry, System.Data.Metadata.Edm.MetadataArtifactAssemblyResolver resolver) + 0x40 bytes
System.Data.Entity.dll!System.Data.EntityClient.EntityConnection.SplitPaths(string paths) + 0x294 bytes
System.Data.Entity.dll!System.Data.EntityClient.EntityConnection.GetMetadataWorkspace(bool initializeAllCollections = false) + 0x95 bytes
System.Data.Entity.dll!System.Data.Objects.ObjectContext.RetrieveMetadataWorkspaceFromConnection() + 0x1b bytes
System.Data.Entity.dll!System.Data.Objects.ObjectContext.ObjectContext(System.Data.EntityClient.EntityConnection connection, bool isConnectionConstructor = false) + 0xad bytes
System.Data.Entity.dll!System.Data.Objects.ObjectContext.ObjectContext(string connectionString, string defaultContainerName = "MyEntities") + 0x1c bytes

Es decir... que fallaba en el método GetManifestResourceNames, quejándose de que ese método no está soportado cuando se invoca sobre un ensamblado dinámico. El ensamblado llamador no era dinámico, de eso estoy seguro. Así como que estoy seguro de que no había ensamblados dinámicos ni como referencia del ensamblado que lo llamaba, ni en la carpeta bin. Pero sí que los había en el dominio de aplicación (AppDomain), con lo cual, a la hora de localizar los recursos, estaba buscando en los ensamblados ya cargados en el AppDomain, y lo estaba haciendo antes de mirar en los ensamblados del directorio de trabajo, ya que si no, habría localizado los recursos sin problemas.

El hecho de que el número de excepciones por constructor fuera aumentando quedaba resuelto de manera sencilla. Según se fuesen generando más ensamblados dinámicos y se fuesen cargando en el AppDomain, se lanzaba una excepción por cada ensamblado dinámico en el AppDomain en cada invocación del constructor.

Vale, ya sé que generar ensamblados dinámicamente (Reflection.Emit) no es algo que se haga todos los días. Pero me ha hecho pensar un poco en cómo escribir las cadenas de conexión para que .NET vaya directamente al ensamblado que tiene incrustados los recursos, y no se de un garbeo cargando todos los assemblies del lugar para buscar un recurso.

Corolario: Cuando especifiques una cadena de conexión de Entity Framework, si está incrustada como recurso en un ensamblado (que es la opción por defecto), indica siempre el nombre del ensamblado donde está ese recurso. ¡Huye de los asteriscos, no suelen ser buenos compañeros! (y si no, se lo preguntas al que hizo un rm -rf * sobre /).

Metadata=res://<assemblyFullName>/<resourceName>

P.D.: Aunque en la documentación diga que tienes que poner el nombre completo del ensamblado, en realidad no es así. No es necesario especificar la versión, ni la cultura, ni la clave pública del ensamblado...

Si sigo así, la próxima entrada será en el 2012... Saludos a todos!

Publicado 13/11/2009 16:24 por Augusto Ruiz
Archivado en: ,

Hola!

Tengo una confesión hoy. Me encanta usar la clase genérica Dictionary. Cuando necesito organizar una caché que pueda ser accedida a la velocidad del rayo, y con claves no necesariamente sencillas, ¡Zas! Dictionary que te crió. Sin embargo, la clase genérica Dictionary implementa la interfaz IDictionary, y no se puede serializar usando IXmlSerializable.

Y... ¿a quién le importa esto? Pues... teniendo en cuenta que IXmlSerializable es la interfaz mediante la cual se serializan los objetos cuando se van a usar Web Services no-WCF (WCF utiliza DataContractSerializer), la cosa parece ya un poco más importante...

Si lo pensamos un momento, serializar un diccionario a XML no es más complicado que tomar las claves y los valores, y plantarlos en un XML. A la hora de deserializar, vas metiendo cada elemento con su clave en el diccionario y listos... Entonces... ¿por qué no se puede serializar usando IXmlSerializable?

La respuesta a esta pregunta la da Kim Hamilton (de Microsoft) en los foros. Básicamente dice que la clase Dictionary es parte de mscorlib.dll y la interfaz IXmlSerializable está definida en System.Xml.dll. Para evitar una referencia circular, el diccionario no se serializa, y listos...

Pues vaya, no?

Por suerte, la gente no es que esté muy contenta con esto, y hay alguna implementación de diccionarios que sí que son serializables, y son tan flexibles como el Dictionary que viene en el framework. Por ejemplo, ésta.

Si estás creando Web Services con WCF, entonces no tendrás problemas, ya que DataContractSerializer serializa sin problemas los diccionarios.

Saludos!

...pero es un buen comienzo (aunque, como todo, depende de para qué).

Llevo varias semanas rumiando esta idea, y la verdad es que me ganaré las iras de más de uno al expresarla en público, pero da igual. Así además podré sondear las ideas de los cracks que por aquí suelen pasar.

Después de llevar ya un buen tiempo trabajando con Web Services, hay una idea que siempre pasa por mi cabeza de manera recurrente. ¿Por qué XML para el intercambio de datos?

Sé que SOA no es lo mismo que XML/XSD, pero en el 95% de los casos suelen ir de la mano para la comunicación de datos entre diversos sistemas. Y mi pregunta es ¿por qué?. Parte de las respuestas que me vienen a la cabeza son las siguientes:

  • XML es un lenguaje extensible, y muy flexible, y XSD nos permite definir cualquier estructura de datos que necesitemos.
  • Es fácil de comprender por los desarrolladores.
  • Es fácil de manipular por parte de los desarrolladores.
  • Al ser una representación textual de los datos, podemos examinar los contenidos de los datos de forma sencilla a la hora de depurar
  • Nos independizamos de las representaciones internas de los datos de cada uno de los sistemas.

Bueno, como podéis ver, la quinta razón no es del todo cierta, ya que si por ejemplo queréis consumir un servicio web creado con otra tecnología que no sea .NET desde una aplicación .NET veremos que cuando determinados tipos de datos aparecen en escena (por ejemplo, fechas), es posible que nos encontremos con algún problema.

Y hay algo más que salta a la vista tras ver las razones expuestas (si bien es una visión algo sesgada): El foco está puesto en el desarrollador, no en los sistemas que tienen que intercambiar los datos entre sí.

La duda que se me plantea es: ¿Por qué usamos un formato de datos comprensible por los humanos cuando los que tienen que manipular esos datos son sistemas informáticos? (es decir, máquinas). Los datos en XML llevan asociados una merma de rendimiento derivada del procesamiento que hay que hacer antes de poderlos utilizar nada despreciable. Y también se usa más espacio del que se podría utilizar, incrementando el gasto de ancho de banda para las comunicaciones.

Creo que sería más interesante algo como lo siguiente: Al igual que tenemos los XSD para definir los XML, ¿no podríamos definir un lenguaje del estilo de XSD que lo que defina es un formato binario de datos de forma sencilla? Es decir, el lenguaje que define los datos a intercambiar debería ser fácil de comprender por los desarrolladores, pero las estructuras de datos que representa deberían ser mucho más compactas y eficientes. Y sin dejar huecos para las imprecisiones. Cada tipo de dato "simple" - fechas, cadenas de texto, datos numéricos - debería tener definida su representación en binario. Por ejemplo, podríamos definir que las cadenas de texto están representadas mediante Unicode, ANSI Z, etc. O si queremos que los datos utilicen big-endian o little endian...

Por supuesto, debería tener la posibilidad de hacer includes, y referencias a objetos, de manera que cada tipo de datos "complejo" pueda ser definido una única vez, y cada instancia pueda ser serializada también de manera única.

Esto requiriría un apoyo por parte de las herramientas de desarrollo, de manera que a la hora de depurar tomasen la estructura de datos a examinar, y utilizando el lenguaje de definición mostrase al desarrollador los datos de una manera sencilla de comprender y manipular.

Quizá con algo así, podríamos tener un "Biztalk" que no necesitase tanto maquinón para ejecutarse... ^_^U

¿Qué opináis al respecto?

Publicado 2/1/2008 11:59 por Augusto Ruiz | 9 comment(s)
Archivado en:

Buenas!

En este artículo vamos a crear un formulario que muestre un mensaje de espera, por si tenemos que hacer una tarea que requiera tiempo, para distraer un poco al usuario. Además vamos a realizar un fundido del formulario, para que aparezca poco a poco.

En este ejemplo vamos a utilizar multithreading, al estilo de lo comentado en este artículo, (no me preguntéis dónde está el código fuente de este artículo, porque pese a que los adjuntos están en el servidor - no sé dónde - no se muestran en los artículos - ayudaaa!).

Antes de entrar en la chicha, unos pocos preparativos:

  1. Vamos a crear un nuevo formulario en nuestro proyecto. Le llamaremos FrmEspera.
  2. A este formulario le quitamos el borde (FormBorderStyle=None).
  3. Además, le quitamos el texto (Text=""). Así conseguiremos que se oculte completamente el borde del mismo, quedando como un panel flotante.
  4. Por último, establecemos la propiedad StartPosition=CenterScreen, para que cuando mostremos el formulario aparezca siempre en el centro de la pantalla.
  5. Para que quede bonito, le ponemos una imagen de fondo al formulario, un control PictureBox para mostrar un icono, y una etiqueta (control Label).

Ahora, vamos al grano: Sería interesante poder modificar el texto a mostrar en el formulario (para usarlo en diferentes situaciones), y la imagen a mostrar. Para esto, en el código del formulario declaramos un par de propiedades:

Public Property ImagenAMostrar() As Image
   Get
      Return pictureBox1.Image
   End Get
   Set(ByVal value As Image)
      pictureBox1.Image = value
   End Set
End Property
 
Public Property MensajeAMostrar() As String
   Get
      Return label1.Text
   End Get
   Set(ByVal value As String)
      label1.Text = value
   End Set
End Property

Como vamos a utilizar varios hilos, vamos a crear un tipo delegado que nos va a permitir invocar desde el hilo en el que vamos a controlar el cambio de la opacidad del formulario el método que realmente cambiará la opacidad del mismo. Esto es necesario para evitar excepciones derivadas del hecho de cambiar una propiedad de un formulario desde un hilo diferente a aquél en el que se creó.

Public Delegate Sub SetOpacityDelegate(ByVal op As Double)
Private dlg As New SetOpacityDelegate(AddressOf Me.SetOpacity)
 
Private Sub SetOpacity(ByVal op As Double)
   Me.BringToFront()
   Me.Opacity = op
   ' Esto es feo, pero necesitamos que se procesen los eventos pendientes...
   Application.DoEvents()
   If op = 0.0F Then
      Me.Close()
   End If
End Sub

Por último, vamos a crear los métodos que usaremos para mostrar y ocultar el formulario. Les vamos a llamar FadeIn y FadeOut:

Public Sub FadeIn()
   Me.Opacity = 0.0F
   Me.Show()
   Dim dlg As New System.Threading.ThreadStart(AddressOf Me.doFadeIn)
   dlg.Invoke()
End Sub
 
Public Sub FadeOut()
   Dim dlg As New System.Threading.ThreadStart(AddressOf Me.doFadeOut)
   dlg.Invoke()
End Sub
 
Private Sub doFadeIn()
   For op As Double = 0.1F To 1.0F Step 0.1F
      If InvokeRequired Then
         Me.Invoke(dlg, op)
      Else
         Me.SetOpacity(op)
      End If
      System.Threading.Thread.Sleep(50)
   Next
   Me.Invoke(dlg, 1.0F)
End Sub
 
Private Sub doFadeOut()
   For op As Double = 1.0F To 0.1F Step -0.1F
      If InvokeRequired Then
         Me.Invoke(dlg, op)
      Else
         Me.SetOpacity(op)
      End If
      System.Threading.Thread.Sleep(50)
   Next
   Me.Invoke(dlg, 0.0F)
End Sub

Como podemos ver, en el método FadeIn y en el método FadeOut, hacemos prácticamente lo mismo: Inicializamos un hilo que llamará al método que se encarga de realizar un bucle variando la opacidad del formulario. Y en este método hacemos un bucle en el que se comprueba si estamos en el hilo de la interfaz de usuario o en otro (llamando a InvokeRequired), si bien no es necesario hacerlo, ya que esa condición siempre será verdadera por cómo llamamos a los métodos doFadeIn y doFadeOut, y se llama al método que cambia la opacidad a través de un delegado si estamos en un hilo diferente, o directamente si estamos en el hilo de la interfaz de usuario.

¿Cómo usaríamos este formulario? Pues de la siguiente manera:

Dim frmMsg As New FrmEspera
frmMsg.MensajeAmostrar = "Por favor, espere. Estoy haciendo algo MUY importante"
frmMsg.ImagenAMostrar = Me.ImageList1.Images("ImagenEspera")
frmMsg.FadeIn()
 
' Hacemos la tarea pesada aquí...
 
frmMsg.FadeOut()

Bueno... Pues eso es todo por hoy.

Saludos!

Hola,

Hace ya poco más de un año, este blog se inauguró con este post. Desde entonces se han recogido 36.232 visitas. Con un total de 46 artículos, se han producido 214 comentarios, una media de 4,7 comentarios por artículo (si bien esto no es lo habitual). Esta media es superior a lo que debería haber sido, principalmente a causa de un par de artículos que fueron en su día polémicos (éste y éste).

De todas maneras, ha sido un año complicado, que se ha notado en la cantidad de artículos publicados por mes, pero haciendo balance, creo que ha sido positivo.

Muchas gracias a todos los que participáis, bien sea leyendo o haciendo comentarios.

Saludos!

La empresa para la que trabajo (Informática El Corte Inglés) va a participar como patrocinador Oro en la III Conferencia de Usuarios de Sharepoint, que se celebrará el 30 de octubre en el Hotel Meliá Castilla, sito en la calle Capitán Haya, 43, en Madrid.

En esta conferencia se hará una presentación de un proyecto bastante interesante, con el que se ha alcanzado la certificación oficial "AA" de Accesibilidad.

Podéis consultar la agenda del evento aquí:

http://www.microsoft.com/spain/office/ceus/agenda/default.mspx

Y podéis inscribiros aquí:

http://www.microsoft.com/spain/office/ceus/default.mspx

Saludos!

Hola a todos!

Como ya he comentado en otra ocasión, estoy usando el Visual C++ 2005 Express para desarrollar un jueguecillo con OpenGL y SDL. Cuando empecé, lo estaba desarrollando con el Visual C++ 2003, pero me comentaron que la STL había mejorado en la versión 2005, y como había una versión gratuita, decidí actualizarme.

El caso es que el otro día decidí liberar una alpha para los betatesters. Y de repente algunos (no todos) me comentaron que no funcionaba. Que les daba el siguiente error:

No se pudo iniciar la aplicacion porque su configuracion es incorrecta. Reinstalar la aplicacion puede solucionar el problema.

Hmmm... Al principio pensé que debía haber algún problema con mi código. Así que empece a meter trazas y pedir logs. Tras comentarme los betatesters que no había ningún log, me di cuenta de que la aplicación, como dice el error claramente, no llegaba a iniciarse. ^_^U

Entonces me di cuenta de un pequeño detalle. El runtime de C de esta versión de C++ probablemente no lo tenían todos los usuarios. Googleando un poco, ví que los betatesters se podían instalar el paquete de distribución del runtime de C++... Pero me pareció que iba a ser un rollo para ellos tener que andar instalando nada para probar una alpha.

¿Qué hago? Si no quiero que se instalen el paquete de distribución, tampoco me voy a poner a generar un msi con la alpha del juego. Habría sido la mejor solución, pero generalmente la gente prefiere descomprimir el archivo, y ejecutar directamente, sobre todo cuando algo es no definitivo...

Finalmente, encontré la solución en los foros de microsoft. La traduzco a continuación:

1) En la máquina en la que tienes instalado Visual C++ Express, crea la siguiente carpeta y subcarpetas colgando de %PROGRAMFILES%\Microsoft Visual Studio 8\VC

redist\x86\Microsoft.VC80.CRT

2) Copia msvcr80.dll, msvcp80.dll, msvcm80.dll de:

%WINDIR%\winsxs\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0de06acd

a:

%PROGRAMFILES%\Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT

3) En la carpeta Microsoft.VC80.CRT, crea un nuevo fichero llamado:

Microsoft.VC80.CRT.manifest

4) Dicho fichero deberá tener el siguiente contenido:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright © 1981-2001 Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <noInheritable/>
  <assemblyIdentity
      type="win32"
      name="Microsoft.VC80.CRT"
      version="8.0.50608.0"
      processorArchitecture="x86"
      publicKeyToken="1fc8b3b9a1e18e3b"
    />
  <file name="msvcr80.dll"/>
  <file name="msvcp80.dll"/>
  <file name="msvcm80.dll"/>
</assembly>

Con esto hemos creado un nuevo directorio en tu instalación de Visual C++ Express que puede ser reutilizado siempre que lo necesites.

Para distribuir estos ficheros, simplemente copia la carpeta Microsoft.VC80.CRT en la carpeta de tu programa. Y eso es todo. Así que, por ejemplo, si el ejecutable de tu aplicación está en la ruta:

%PROGRAMFILES%\MiAplicación

tendrás una carpeta llamada:

%PROGRAMFILES%\MiAplicación\Microsoft.VC80.CRT

que contendrá los cuatro ficheros mencionados anteriormente (3 DLLs, y el manifiesto que has creado a mano).

No hace falta instalar nada más. Simplemente arranca tu aplicación, y se ejecutará sin problemas. No hace falta ninguna instalación específica. Simplemente basta con que tu programa de instalación cree esa subcarpeta junto con el resto de archivos que tenga tu aplicación.

NOTA:

%WINDIR% es el directorio en el que está instalado Windows. Es una variable que se puede utilizar en el cmd.exe. Normalmente es C:\WINDOWS

%PROGRAMFILES% es similar, salvo que apunta a la carpeta Archivos de Programa. Lo bueno es que es independiente de idioma.

Saludos!

No os llevéis a confusión con el título... Este artículo no es más que un mini-tutorial sobre la clase System.IO.Compression.GZipStream, que nos proporciona la funcionalidad necesaria para comprimir datos utilizando el estándar "de facto" gzip.

La verdad es que el uso de esta clase no debería ser complicado, ya que básicamente lo que hacemos es leer y escribir datos de un stream, como si fuese un fichero o un trozo de memoria más. Pero tiene una pequeña particularidad: Para que funcione correctamente nuestro código tenemos que tener cuidado de indicar en el constructor que queremos que el stream subyacente a nuestro GZipStream se quede abierto cuando terminemos de utilizarlo. Sobre todo cuando lo que queremos es comprimir en memoria, para posteriormente, sin crear ficheros temporales en el disco duro, enviarlo por red.

Veamos el ejemplo, sencillo, sencillo:

Para comprimir:

   Dim memStream As New System.IO.MemoryStream
   ' Aquí está el quid de la cuestión: Indicarle en el tercer parámetro
   ' que cuando cierre el stream de compresión, no elimine el stream
   ' subyacente.
   Dim gzip As New System.IO.Compression.GZipStream(memStream, _
                   IO.Compression.CompressionMode.Compress, True)
   Dim fileBytes() As Byte
   Dim compressedFileBytes() As Byte = {}
 
   ' Leemos el contenido del fichero a comprimir
   fileBytes = System.IO.File.ReadAllBytes(filePath)
   ' Escribimos en el stream compresor
   gzip.Write(fileBytes, 0, fileBytes.Length)
   ' Si no cerramos el stream, no podremos leer correctamente el 
   ' resultado de la compresión.
   gzip.Close()
 
   ' Vamos a leer los datos comprimidos
   Array.Resize(compressedFileBytes, memStream.Position)
   ' Colocamos el stream de memoria al inicio, para leer todo.
   memStream.Position = 0
   ' Ya tenemos todos los datos en el array compressedFileBytes.
   memStream.Read(compressedFileBytes, 0, compressedFileBytes.Length)
 
   ' Hacer lo que queramos con los bytes comprimidos...
 
   ' Y finalmente, limpiar un poquito.
   memStream.Close()
   gzip.Dispose()

Para descomprimir:

  ' fileBytes es un array de bytes que contiene el fichero comprimido
  Dim memStream As New System.IO.MemoryStream(fileBytes)
  ' Creamos el GZipStream basándonos en el stream de memoria.
  Dim gzip As New System.IO.Compression.GZipStream(memStream, _
                   IO.Compression.CompressionMode.Decompress)
  ' Si sabemos a priori el tamaño del fichero sin comprimir, 
  ' inicializamos el array de bytes que contendrá el fichero
  ' descomprimido.
  Dim realFileBytes(fileSize - 1) As Byte
 
  memStream.Position = 0
  
  ' Leemos el fichero.
  gzip.Read(realFileBytes, 0, fileSize)
  ' Cerramos...
  gzip.Close()
  gzip.Dispose()
 
  ' Usamos el fichero para lo que queramos, está en el memoryStream...
 
  ' Cerramos el MemoryStream.
  memStream.Close()

Saludos!

Publicado 15/10/2007 14:00 por Augusto Ruiz | 1 comment(s)
Archivado en:

Iba a poner "para redimensionar imágenes", pero realmente no es eso lo que hace. Lo interesante de esta técnica es que cuando reduces la imagen, lo que hace es eliminar la parte de la imagen que no nos proporciona información interesante. Y funciona bastante bien.

A esta técnica la llaman "Seam Carving".

Un vídeo de demostración de esta técnica que podéis ver en YouTube:

Uno de los impulsos más difíciles de reprimir para un programador es el de ver un código antiguo, de esos que los años han ido degradando, y no caer en la tentación de rehacer esa funcionalidad que se nota que tiene ñapa sobre ñapa, que se hace de manera ineficiente, que tiene cincuenta mil ifs anidados...

Por otra parte, cada cierto tiempo, las empresas se plantean realizar migraciones de aplicaciones antiguas a una tecnología más nueva. Los motivos esgrimidos suelen ser aprovechar una infraestructura que ha ido mejorando con el paso del tiempo, que puede soportar un mayor número de usuarios, o mayor carga de trabajo.

Sin embargo, el software de dichas empresas puede no haber crecido a la par, por ejemplo porque cuando se desarrolló la aplicación no había esas necesidades, y no se incluyeron como requisitos no funcionales.

Ese es el momento en el que, armados con todas las herramientas estupendas que tenemos a día de hoy, te encuentras con la "fácil" tarea de realizar la migración. Y ya que estamos, como hacer esa migración es "tan sencillo", pues ya aprovechamos y tenemos que incluir cierta funcionalidad que no tenía la aplicación original. Como todos sabemos, una migración es algo que "no produce valor añadido" - dicen - y claro, puede tener un cierto coste que, elevado o no, hay que justificar. Y de ahí el hecho de meterle "algo nuevo".

¿Quién no se ha encontrado con una situación así?

Tras realizar la migración, el resultado habitual, y que sorprende a muchos clientes, es el siguiente:

  • La aplicación migrada suele tener más bugs que la original. Normalmente suele ser porque ese código tan feo suele cubrir los cincuenta millones de casos especiales que se presentan durante el proceso que cubre, y al que nadie hace caso en el momento de plantear la nueva solución. Generalmente, no es posible bajar al nivel del código para todo el sistema antiguo cuando se diseña el nuevo.
  • El rendimiento de la nueva aplicación suele ser menor. Esto pasa más a menudo de lo que pudiera parecer, por ejemplo cuando se migra una aplicación en entorno host (cobol) a tecnologías actuales (Java - .NET). Cierto, la tecnología moderna te proporciona mucha más funcionalidad de base, pero a un cierto precio. Acelera el desarrollo, pero no necesariamente el rendimiento.

De ahí que surja la pregunta: ¿realmente es rentable hacer una migración de una aplicación desde cero?

Mi opinión es que depende. Hay situaciones en las que puede interesar migrar. Pero en la mayoría de los casos, puede ser interesante pensar en la reutilización de ese sistema, por ejemplo aplicando una arquitectura orientada a servicios. La nueva funcionalidad se puede desarrollar con otra tecnología más actual, y ciertas partes del sistema antiguo se pueden ir migrando paulatinamente.

Y vosotros, ¿qué opináis?

P.D.: Al hilo, un artículo muy interesante.

Bueno, bueno, bueno...

En un proyecto en el que se querían ahorrar unas pelillas unos eurillos (esa cabeza...) en licencias (y gastarse muchas muchos más en gente de sistemas, supongo...) me pidieron evaluar la posibilidad de crear una aplicación que se pudiese actualizar automáticamente, de forma sencilla. Por mi deformación profesional, mi cabeza pensó directamente en ClickOnce. Claro, si publicar con ClickOnce está chupado, y cada vez que se publica una nueva versión, la aplicación se puede actualizar automáticamente! (depende de cómo lo configures, claro...).

Pero luego me dí cuenta de un pequeño problema. Para poder tener esa aplicación disponible necesito un IIS para que las aplicaciones puedan comprobar si hay una nueva versión... O no?

Pues no.

Empecé a pensar un poco sobre cómo debería funcionar ClickOnce. El cliente se descarga un fichero .application desde una URL, que no es más que un fichero XML firmado que le indica, entre otras cosas, qué manifiesto de aplicación es el que tiene que procesar para obtener los ficheros de la aplicación actual. Así que pide al servidor web dicho fichero (un .manifest). Este fichero vuelve a ser un XML firmado donde aparecen una lista de ficheros etiquetados, con el hash de cada fichero.

Para cada fichero, el cliente comprueba si ya tiene el fichero (en el caso en el que se haya instalado otra versión previamente). Si lo tiene calcula el hash del fichero en local, y si coincide con el que le indica el manifiesto, no se lo descarga. Si difiere o no lo tiene, le pide el fichero al servidor, lo descarga, calcula el hash del fichero, y si coincide con el que indica el manifiesto continua con el proceso (y si no coincide, no deja continuar la instalación). Por lo tanto... ¿cuál es el papel del servidor en todo este trajín? No es más que un repositorio de ficheros. El cliente es el que realmente realiza todo el trabajo y le va pidiendo los ficheros que necesita para instalar o actualizar la aplicación.

Por lo tanto... Esto debería funcionar con cualquier otro servidor web... ¿O no?. Decidí hacer una prueba rápida con un Apache. Coloqué los resultados de una publicación en un directorio virtual, y abriendo un Internet Explorer le indiqué la URL del fichero .application. Y claro, no funcionó. No funcionó porque el Apache no tenía los tipos mime configurados correctamente. Es decir, para que el Internet Explorer sepa cómo tiene que procesar el fichero .application, es necesario que el servidor web le indique de qué tipo es el fichero que le está enviando. Es como cuando queremos volcar un PDF directamente por el stream de salida del objeto response, que tenemos que poner las cabeceras apropiadas.

Así que, creamos un fichero de texto con el nombre .htaccess, que contenga las siguientes líneas:

AddType application/x-ms-application application
AddType application/x-ms-manifest manifest

AddType application/octet-stream deploy

Y listo! Funciona de maravilla!

Por cierto... Sobre cómo solucionar problemas de ClickOnce, podéis ver más información aquí.

 

Saludos!

P.D.: Muchas gracias a Daniel Celemín (Lord Fred) por la estupenda caricatura :)

Muy buenas!

Hace mucho, mucho tiempo, en una galaxia muy lejana... Esto... Bueno, que hace demasiado tiempo desde mi último post. Demasiados cambios y muy poco tiempo para contarlos. Pero vamos a lo que vamos...

Ayer tuve el placer de asistir al Innovation Days en Madrid, donde José Murillo, Isabel Gómez, César de la Torre, Daniel López, Miguel Ángel Ramos (en sustitución de Alejandro Mezcua) y Unai Zorrilla nos estuvieron mostrando las novedades que vamos a poder disfrutar en breve, (bien sea con Longhorn, Orcas o Silverlight), además de poder ver un poco de ASP.NET AJAX y una interesante charla sobre SaaS.

Las charlas, como siempre, me dejaron con ganas de más. Y es que sólo un día para tratar sobre todos estos temas es demasiado poco... Pero en fin, que te despiertan el gusanillo para cacharrear.

Saludos!

¡Hola a todos!

 Estoy en proceso de mudanza. Me he cambiado de casa (aún de alquiler, pero todo llegará...) y en la casa nueva no tengo internet, con lo que me es difícil hacer las cosas habituales (es decir, actualizar este blog y el de historias de un consultor).

Aun así, estoy aprovechando un poco el tiempo leyéndome el libro CLR via C#, que me ha llegado de Amazon hace bien poquito, junto con otro libro menos conocido, el Programming Challenges de Steven S. Skiena y Miguel A. Revilla. Muy recomendable para repasar conceptos de estructuras de datos, y aprender a pensar un poco más al estilo de Programancia101.

Aprovechad ahora los que estéis en zona Euro, que como el Euro está fuerte, los libros en Amazon salen tirados!!! (los dos por poco más de 60€) 

Saludos!!!

Publicado 9/4/2007 9:55 por Augusto Ruiz | con no comments
Archivado en:

Bajo la apariencia de una página muy sencillita, en el MSDN tenemos disponible una serie de tutoriales bastante completos sobre cómo realizar aplicaciones en tres capas utilizando ASP.NET 2.0.

Elige tu sabor favorito (C# o VB.NET), y prepárate para aprender en 17 tutoriales cómo organizar una aplicación como dios manda.

Saludos!

Publicado 7/3/2007 13:45 por Augusto Ruiz | 3 comment(s)
Archivado en:

Hola a todos,

Sigo con mis proyectos en casa, para el desarrollo de un juego (y de varias herramientas). En http://cezgs.computeremuzone.com/ estamos desarrollando cositas (top secret hasta que se publiquen, sorry), y alguna vez desarrollo alguna herramienta interna.

Ahora mismo estoy enfrascado con un tracker que permita componer melodías para ordenadores de 8 bits. De momento está enfocado al chip AY que traían los CPC (todos) y Spectrum de 128kb.

Así que buscando por la red, encontré una librería que emulaba el comportamiento de dicho chip, escrita en C. En Windows no compilaba (hasta que añadí un #define en un fichero .h), y cuando lo tuve funcionando, la migré a C#.

La verdad es que mientras la migraba me sorprendí de lo sencillo que era. Y podéis descargar el código fuente de dicho emulador, en C#, haciendo click aquí.

Saludos!

Publicado 2/3/2007 12:06 por Augusto Ruiz | 2 comment(s)
Archivado en: ,,

Hola a todos!

Desde hace bastante tiempo estoy desarrollando un juego en PC usando C++ y OpenGL. (¡No, no me tiréis piedras, no usar DirectX tiene una explicación!). Así que este fin de semana, después de desesperarme con el Dev-CPP, decidí cambiarme y usar el Visual C++ 2005 Express.

La razón de no usar DirectX es que para dominar el mundo conseguir la mayor distribución posible necesito que el juego sea multiplataforma. De ahí que use OpenGL, SDL y C++. :P

Ya tenía experiencia con el Visual C++ 6 y con el Visual C++ 2003, así que sabía que lo que me iba a encontrar era conocido, y probablemente podría aumentar mi productividad.

También tenía algunas dudas sobre si hacer el cambio o no, sobre todo después de leer los posts de Rafael Ontivero, pero como este proyecto es propio, consideré que no corría riesgos por probarlo.

A la hora de configurar el entorno de desarrollo, todo ha ido como la seda. Directorios para includes, librerías, etc... Estupendamente. Terreno conocido. :)

Hay un par de cosas que no me han gustado de esta versión:

  • A la hora de compilar, siempre genera la versión Release.
  • Cuando pulsas F5, siempre genera la versión Debug.
  • No he visto forma humana de hacer refactoring.
  • El Intellisense funciona cuando quiere, y sólo sobre lo que hubiese ya compilado al cargar el proyecto. Posteriores compilaciones añadiendo métodos, propiedades, etc, etc, no hacen que se refresque el Intellisense (o eso me ha parecido).

Si lo pienso fríamente, tiene sentido. Pero no me deja elegir. El combo para elegir la configuración de generación de assemblies está deshabilitado, y la verdad es que no se si está capado para la versión Express (en cuyo caso, sería mejor que no apareciese en absoluto) o que soy un zote y no he encontrado aún la forma de habilitarlo (esto será lo más probable...)

En cuanto al rendimiento de la aplicación, no he notado mejora alguna, pero tampoco pérdida alguna... Teniendo en cuenta que el trabajo duro lo hace OpenGL, es lo razonable ;)

Pero claro, el poder disponer de una herramienta como ésta, y gratuita, es algo muy, muy interesante. A aquellos frikis como yo que quieran usar C++ para proyectos personales, se lo recomiendo encarecidamente. Pero sólo en el caso de que sean aplicaciones no .NET, ya que la productividad de C# sigue estando a años luz que la de C++ (al menos para mí).

Saludos!

Publicado 13/2/2007 11:52 por Augusto Ruiz | 12 comment(s)
Archivado en:

En el blog de Metaemotion hay un artículo interesante (en castellano) en el que se detalla cómo se puede eliminar la restricción de tamaño mínimo que tienen impuestas por defecto las ventanas de Windows.

Lo que no se comenta en ese artículo es que podemos aprovechar esa misma técnica para cambiar la restricción de tamaño mínimo cuando esta esté entre 0 y 112 píxeles (para el resto de tamaños se puede usar la propiedad MinimumSize de la clase System.Windows.Forms.Form).

La lectura es más que recomendable ;)

Saludos!

Hola!

Curioseando un poco por la red, he encontrado un artículo muy interesante en #flipcode, en el que el autor comenta cómo se puede crear un sistema de reglas utilizando .NET que tome decisiones basándose en una máquina de estados.

Realmente esto no es novedoso, pero me ha llamado la atención el sistema tan sencillo que ha creado el autor para componer reglas básicas (lo que en el artículo llama primitivas).

Así que si tenéis que hacer una máquina de estados, os recomiendo que lo peguéis una ojeada.

Saludos!

UPDATE: Obviamente, esto no os será interesante si estáis utilizando Workflow Foundation... Pero por desgracia no todo el mundo puede usar .NET 3.0 ;)

A petición de espinete escribo una segunda parte del artículo, en el que adjunto el código fuente de ejemplo con lo que se proponía en el artículo anterior.

En el código adjunto se pueden ver las siguientes clases:

  • Trabajador: Esta clase es la que tiene implementado el proceso pesado. En nuestro caso, simplemente irá desde un número especificado como mínimo hasta el máximo, esperando un segundo en cada iteración.
  • GestionTrabajadores: Esta clase es el singleton al que se refiere el artículo anterior. Se encarga de gestionar una tabla hash en la que las instancias de la clase Trabajador se identifican mediante un Guid.

Además, podemos ver que hay tres páginas en el proyecto Web:

  • Inicial.aspx: En esta página se crea una instancia de la clase Trabajador, se la inicializa, y se la añade a la clase GestionTrabajadores, tras haber creado un Guid para identificarla. Posteriormente se reenvía al usuario a la siguiente página, pasando el Guid de la petición actual.
  • EnProceso.aspx: En esta página se muestra el progreso de la petición actual. Si se detecta que la petición ha finalizado, se reenvía al usuario a la siguiente página.
  • Terminado.aspx: En esta página simplemente se muestra un mensaje que indica que el procesamiento ha finalizado.

Obviamente, las mejoras que se le pueden hacer a este ejemplo son innumerables, pero aquí dejo un par de ideas, a ver si alguien se anima y las realiza, aportándolas para la comunidad:

  • La página Inicial.aspx, en vez de notificar el GUID de la petición, podría enviarnos a la página EnProceso.aspx, y en esta página que se muestren los diferentes trabajos que hay en curso. Para ello bastaría con proporcionar un enumerador a la clase GestionTrabajadores para recorrer la tabla hash.
  • La página EnProceso.aspx podría utilizar AJAX para no tener que refrescarse cada dos segundos.

Bueno, pues esto es todo por hoy.

Saludos!

Hola!

En CodeProject tenéis disponible un ejemplo de cómo crear un gadget para Windows Vista. Este gadget muestra la tira de Dilbert, y nos permite mostrar las tiras anteriores.

El artículo lo podéis leer aquí.

Saludos!

Publicado 1/2/2007 19:24 por Augusto Ruiz | con no comments
Archivado en: ,
Más artículos Página siguiente >