Mostrando las entradas con la etiqueta desarrollo. Mostrar todas las entradas
Mostrando las entradas con la etiqueta desarrollo. Mostrar todas las entradas

Top 10 de cosas que molestan a los programadores

viernes, 29 de octubre de 2010
Este post fue escrito por Kevin William Pang en Agosto del 2008, al ver su contenido me identifique en cada una de sus palabras y me vi obligado a pedirle su autorización para compartir este top 10 en español.
La versión en ingles esta aquí

10. Comentarios que explican el "como" y no el "porque"
Los niveles introductorios de programación siempre recomiendan a los estudiantes que deben poner comentarios a su código constantemente. La idea es la de tener tantos comentarios como se puedan en vez de no tener ninguno. Desafortunadamente algunos programadores se toman muy a pecho este precepto y comentan cada linea de código, es por eso que muchas veces encontraras este tipo de lineas de código.
Se entiende que es lo que hace el código? Si bien hay muchas lineas de comentarios describiendo lo que el código hace, no hay ninguna que diga por que lo hace.
Consideremos la misma porción de código con un enfoque de comentarios diferente:
Mucho mejor! De todas formas no tenemos un entendimiento exacto del propósito del codigo pero ya tenemos un punto de partida para entenderlo.
Los comentarios se suponen que están para ayudar al lector a entender el código, no la sintaxis. Esta bien  asumir que el lector tiene un entendimiento básico de como funciona un loop, no hay necesidad de escribir un comentario como "iterar la lista de clientes". Con lo que el lector no estará familiarizado es por que tu código funciona de la forma en la que lo escribiste.
Snippet extraído del post de Jeff Atwood "Coding without comments"

9. Interrupciones
Muy pocos programadores pueden ir de 0 a codificar en un parpadeo. En general, el programador tiende a ser mas como una locomotora que como un Ferrari; es posible que nos tome un tiempo comenzar a codificar, pero, una vez que empezamos podemos realizar una gran cantidad de trabajo. Lastimosamente es difícil entrar en tal concentración cuando tu tren de pensamientos se ve descarrilado constantemente por clientes, administradores y compañeros.
Simplemente hay demasiada información que mantener en mente mientras trabajamos en una tarea como para poder dejarla, atender otro asunto y luego volver en el punto que habíamos dejado sin perder el ritmo. Las interrupciones matan nuestra concentración y retomarla es una tarea que consume tiempo, frustranrte y lo pero de todo es que produce errores.

8. El monstruo del alcance
[El Síndrome del lavadero o Arrastramiento del alcance (Scope creep en inglés) en gestión de proyectos se refiere a aquellos cambios no controlados en el alcance de un proyecto. Este fenómeno puede ocurrir cuando el alcance de un proyecto no se define, documenta, o controla correctamente.
Típicamente, el aumento del alcance consiste en productos nuevos o nuevas características de productos ya aprobados que hacen que el equipo de proyecto se desvíe de su propósito original. Debido a su tendencia a centrarse en solamente una dimensión de un proyecto, el arrastramiento del alcance puede también dar lugar a que el equipo de proyecto exceda su presupuesto y cronograma originales. Mientras el alcance de un proyecto crece, más tareas se deben terminar con el mismo coste y cronograma que la cantidad original de tareas del proyecto.]
El arrastramiento del alcance puede tornar un requerimiento simple en un monstruo complejo y horrible que consume nuestro tiempo. Es solo necesario un par de "teclazos" por parte del ingeniero de requerimientos para que esto suceda:
  • Versión 1: Mostrar un mapa con la ubicación
  • Versión 2: Mostrar un mapa 3D de la ubicación
  • Versión 3: Mostrar un mapa 3D de la ubicación por el cual el usuario pueda navegar
Lo que habría sido una tarea de 30 minutos se ha convertido en un sistema complejo que puede llegar a tomar cientos de horas hombre. Aun peor, la mayormente los arrastres de alcance ocurren durante el desarrollo, lo que requiere de re-trabajo, refactoring y muchas veces botar código que ya se había escrito días antes.

7. La administración no entiende de programación
La administración/gestión no es un trabajo facil. La gente es dificil; Mantener a un gran grupo de programadores contentos y unidos es una tarea titanica. Sin embargo, eso no quiere decir que la administración deba mantenerse fuera de un entendimiento básico del trabajo que se esta realizando. Cuando la administración no puede entender el desarrollo encarado es muy probable que se arrastren los alcances y fechas limites poco realistas. y, en general, frustración en ambos lados de la mesa. Esta es una queja común entre los programadores. Esta es una queja común entre programadores y también la fuente de muchos miedos 

6. Documentar nuestras aplicaciones
Si, existen aplicaciones que generan la documentación del código que escribimos, esa documentación es buena para otros programadores. Si trabajamos con una aplicación que sera utilizada por gente normal va a ser necesario escribir una documentación que una persona promedio pueda entender (por ejemplo, como funciona la aplicación, manual de usuario, etc.).
No es difícil ver que esta tarea es una carga muy pesada para los programadores. Fíjense en todos los programas Open Source que hay disponibles, cual es el común denominador en las tareas que mas ayuda solicitan? Documentación. Creo podre hablar en nombre de todos los programadores cuando digo "Puede alguien mas hacerlo?"

5. Aplicaciones sin documentación
Nunca se dijo que no somo hipócritas. Lo programadores están siempre solicitando librerías de 3ros para integrar en su trabajo. Y para poder hacer eso "necesitamos documentación". Des afortunadamente, como mencionamos en el punto 6 programadores odian escribir documentación. No, la ironía no se ha perdido.
No hay tarea mas frustrante que tratar de usar una librería de 3ros cuando no tenemos la menor idea de que es lo que hace la mitad de la función que queremos usar. Cual es la diferencia entre funcionPobrementeNombrada() y funcionParecidaPobrementeNombrada()? Necesito verificar nulos antes de usar la PropiedadX? Seguramente sera con prueba y error...

4. Hardware
Cualquier programador que alguna vez fue llamado para depurar un error extraño en la base de datos o por que el controlador RAID no funciona correctamente sabe que los problemas de hardware son dolorosos. Parece haber una mala concepción que si un programador trabaja con computadoras, seguramente sabemos como arreglarlas. Esta bien, esto puede ser cierto para algunos programadores, pero reconozco que la gran mayoría de nosotros no nos importa una vez que el assembly ya esta compilado. Nos concentramos en las tareas de alto nivel de tal forma que las cosas funcionen como deberían en ese estrato.

3. Ambigüedad
"El sitio web no funciona". "La funcionalidad X no funciona correctamente". los requerimientos ambiguos son dificiles de concretar. Va a ser siempre una sorpresa para mi como la gente ajena a la programación se exaspera al tratar de explicar como reproducir un problema a un programador. Pareciera que no entienden que "esta roto, no funciona, arregla lo" no es suficiente información para solucionar el problema.
El software es (mayormente) determinístico. Y nos gusta de esa manera. Dennos el gusto al hacernos saber que paso del proceso esta mal en lugar de pedirnos que "lo arreglemos".

2. Otros programadores
Los programadores no siempre se llevan bien entre ellos. Impactante, pero cierto. Este podría ser fácilmente el #1 así que solo nombrare algunas de las situaciones mas comunes que molestan a sus colegas de programación:
  • Ser gruñón al punto de ser hostil
  • No entender que hay un tiempo para discutir la arquitectura del sistema y tiempo para hacer las cosas.
  • Imposibilidad de comunicarse efectivamente y confundir la terminología
  • Apatía hacia el código base y el proyecto
Y como broche de oro la molestia mas grande para un programador

1. Código propio después de 6 meses
Alguna vez volteaste a ver algún código viejo tuyo con cara de dolor? pero que estupidez! como pudiste? quémalo... quémalo con fuego...!
Buenas noticias, no estas solo.
La verdad es, el mundo de la programación es un mundo en constante cambio. Lo que consideramos buenas practicas hoy puede ser obsoleto mañana. Simplemente no es posible escribir un código perfecto por que los estándares que se usan para juzgar el código están en constante evolución. Es difícil aceptar que to código, por muy lindo que sea hoy, probablemente sea ridiculizado en un futuro. Es algo frustrante ya que no importa cuanta investigación hagamos con las ultimas herramientas y tecnología, diseños, frameworks y buenas practicas siempre vamos a ver que falta algo mas. Para mi, esta es la cosa mas molesta para un programador. La fragilidad de lo que hacemos es necesaria para impulsar a las mejoras, pero no puedo dejar de sentirme como uno de esos monjes que pintan con arena.
Bien, ahí están. Top 10 de cosas que molestan a los programadores. Si ves que falta alguno por favor envía tus comentarios.

Off Topic 1 billion hungry

lunes, 25 de octubre de 2010
Por favor apoya a este movimiento, Únete a la red de lucha contra el hambre.


www.1billionhungry.org/andresurena/ 








Ideas que valen

martes, 23 de marzo de 2010
Las grandes ideas, proyectos novedosos y técnicas revolucionarias para hacer software vienen de todas partes del mundo, pero que pasa con el tercer mundo?

Muchas grandes ideas están flotando en el éter esperando a que alguien las tome y no solo las explote de manera adecuada, sino que también la registre y patente a su nombre, pero, es aquí donde aparece la gran interrogante que hace tirar por el suelo el anhelo de la idea. “es demasiado sencillo para ser una patente”.

FALSO, completa y absolutamente falso, una buena idea no tiene por qué ser complicada, sobretodo en el rubro del desarrollo de software, entonces, que es lo que pasa con los Ingenieros de sistemas y de Software que no están generando evidencia de sus ideas? Fácil es el temor al rechazo, este puede venir de un comité evaluador o de sus propio pares “para que vas a registrar esa idea si es de lo mas básica”, “ese proceso es por demás simple, no amerita una patente” y demás excusas que he escuchado muchas veces.

Que sucede, o estamos muy por encima del resto de los mortales que todo nos parece una banalidad o se nos está escapando la liebre por no tener la suficiente autoestima para decir que nuestras ideas son buenas.

He tenido el honor de trabajar con profesionales excepcionales que han tenido ideas que a nadie más se le han ocurrido, y han dejado que se les vaya la oportunidad de trascender en el tiempo registrando esa idea bajo su nombre, es más, me atrevo a formular la pregunta, ¿A quién no se le había ocurrido antes que una aplicación web sea consumida como un servicio (antes que aparezcan los web services) por ejemplo?

Muchas de estas ideas están siendo desperdiciadas por muchos grandes pensadores del mundo del software, quiero aclarar que no necesariamente los grandes pensadores son viejos barbudos y despeinados que se la pasan 25 horas al día frente a la computadora re-pensando y re-haciendo las cosas una y otra vez hasta llegar a un punto máximo de optimización, he conocido estudiantes de los primeros años que tienen ideas claras, frescas y sobretodo innovadoras de cómo hacer las cosas de forma diferente para aprovechar un aspecto más del software siendo desarrollado.

Para Argentina existe una oficina de Marcas y Patentes (su sitio web http://www.marcasypatentes.net/registrodemarcas.htm) en la que se detalla la forma en la que una idea o producto puede ser patentado.
Para Estados Unidos la oficina de Marcas y Patentes del departamento de comercio del gobierno del mismo país se encuentra en http://www.uspto.gov/
Para México el Registro de marcas, patentes, derechos de autor y dominios esta en http://www.marcas.com.mx/

Cada oficina de registro de marcas y patentes tiene sus propias restricciones para los postulantes, pero, la cosa no es tan fácil si uno trabaja en relación de dependencia en una empresa de software. A veces (y sin el conocimiento del empleado) el propietario de la empresa puede hacer uso y abuso de las ideas y sub-productos que salgan de los proyectos en los que se trabaja, es aquí donde aparece una figura legal que protege a la empresa en esta situación que es el convenio de confidencialidad y propiedad intelectual, hay que conocer bien como está planteado este convenio y su alcance; en el peor de los casos uno no puede ni decir donde trabaja ni cuánto gana, en el mejor de los casos se le permite al empleado compartir conocimientos siempre y cuando no se revele la receta secreta.

Para aquellos que tengan gente a su cargo en un proyecto de software y que de este salgan sub-productos, no les vendría mal que registren esas ideas (no sin antes verificar que otro no la haya registrado antes) posiblemente terminen ganando unos $$$ extras porque algunos de los desarrolladores creó una herramienta muy simple que le ayuda a realizar su trabajo.

Mitos y Leyendas del desarrollo con herramientas .Net

lunes, 22 de marzo de 2010
Desde hace ya un tiempo atrás, vengo enterándome que hay gente que cree que el desarrollo con Microsoft Framework.Net en todas sus versiones es pago y que la licencia es prohibitiva por sus altos costos. Es por tal razón que quiero aprovechar este Post para hablar un poco sobre este mito.

Para empezar bien, cuando una persona descarga el instalador del Framework está aceptando los términos y condiciones de la licencia, cabe destacar que en ningún momento se solicita un pago por la descarga o se evaluara una llave de código al ser instalado, es más, este framework puede ser instalado en una PC que no tenga una versión genuina de Windows (en cualquiera de sus versiones superiores a Win2k).

Esta licencia solicita que no se altere (entre otras cosas bastante básicas) la línea base del framework y que al redistribuirse hay ciertos archivos que no deberán ser empaquetados en la versión final de nuestro software.

Eso va en términos generales, podríamos ir línea por línea del EULA del Microsoft Framework .Net, pero, el desarrollador promedio no verá más puntos de interés en este documento, para poder bajar la versión 3.5 de esta plataforma y leer la documentación pueden visitar el siguiente enlace haciendo click aquí.

Ahora, las herramientas de desarrollo o también conocidos por sus siglas en ingles IDE (Integrated Development Environment) algunos si son pagos como Visual Studio que viene en muchas versiones orientado para diferentes roles dentro del desarrollo de software, desde los desarrolladores, pasando por testers, Arquitectos, profesionales de IT y Project Leaders. Estas herramientas pueden o no ser costosas dependiendo el rol al que están orientadas, pero no son las únicas disponibles para el desarrollo, Microsoft en su afán de expandir su plataforma .Net ha creado herramientas (IDEs) gratuitos, estas son las versiones Express de sus productos, que van desde Microsoft SQL Express para el manejo de datos a pequeña escala hasta IDEs de los lenguajes estrella de la plataforma.

Podemos ver con más detalle estas herramientas siguiendo el siguiente enlace.

Estas herramientas son completamente funcionales.

Espero les haya sido de ayuda.

HttpHandlers implementación básica

lunes, 1 de febrero de 2010
Flexibilidad, palabra clave al construir aplicaciones web, con httpHandlers podemos llegar un poco mas allá con muy poco esfuerzo.

El concepto detrás de los httpHandlers es brindar un punto en el que se pueda re-definir el procesamiento de una solicitud http, esto se presta para la implementación de muchos y diversos esquemas y patrones para la implementación de aplicaciones web.

Un handler tiene 2 caras, una que mira hacia la aplicación web y que es la que ve en detalle que es lo que hace y como, la otra cara mira hacia Internet Information Services, esta cara se encarga de tomar la solicitud http y llevarla al lugar que le corresponde dentro de la aplicación.

La idea del post es dar a conocer como se puede implementar un handler.

Pasos a seguir:


Creación de la clase que implemente la interfaz iHttpHandler (System.Web)
La clase podemos localizarla en 2 lugares, un (el mas cómodo) una clase dentro del directorio App_Code o en su defecto en un proyecto de assembly que haga referencia a System.Web e implemente la interfaz, para cualquiera de los dos casos la clase debería verse así

public class DemoHttpHandler : IHttpHandler
{

    #region IHttpHandler Members

    bool IHttpHandler.IsReusable
    {
        get { return false; }
    }

    void IHttpHandler.ProcessRequest(HttpContext context)
    {
        //Código para manipular el context
    }

    #endregion
}


Escribir el código necesario para la manipulacion del contexto web
El objeto HttpContext trae consigo a los objetos Response, Request entre otros mas que permiten la manipulación de la solicitud http hacia el sitio web, la manipulación no tiene limites (salvo la imaginacion del desarrollador) por lo que no me explayare mucho en esa parte.

Modificación del archivo web.config para apuntar al handler
Como todas las configuraciones de una aplicación .Net, los handlers tienen su sección propia muy sencilla que se ve de la siguiente forma:

Como se puede apreciar un nodo de http handlers es facil de entender, aquí sus partes
verb = verbos que serán interceptados por el handler, GET,POST,PUT, etc.
path = Extensión o nombre de archivo si es necesario que sera interceptado por el handler ni bien se lo solicite
type = El nombre de la clase (si es que la implementamos dentro del App_Code) , el nombre del assembly al que pertenece
validate = en caso de ser verdadero se verificara que la clase pueda ser cargada en memoria aun si no se ha realizado una solicitud, de ser falso se realizara esa verificación en cuanto se haga una solicitud.



Configuración del mapping en el directorio virtual de IIS para controlar las solicitudes http
Esta configuración se la puede realizar en IIS en sus versiones 5.1 o superior y dependiendo de la versión y el idioma la forma de encontrar donde es que se hace esta configuración, pero en términos generales se encuentra siempre en el mismo lugar (cambian ligeramente los nombres de los tabs y botones), y como dicen que una imagen vale mil palabras les paso las imágenes de la configuración del IIS

En el caso de la última imagen existe un check que verifica si el archivo solicitado existe, si es que se va a crear contenido dinámico en nuestro código este check deberá permanecer sin selección.

Con todo esto ya tenemos listo el handler y funcionando perfectamente, lo demás dependerá del código de implementación dentro del método ProcessRequest de nuestra clase.

Compresión de archivos con .Net

miércoles, 27 de enero de 2010
Muchas veces nos vemos en la necesidad de enviar información generada por una aplicación hacia un cliente, pero esta es demasiado grande o tiene demasiadas partes como para realizar una sola tarea para lograr el objetivo, compresión de archivos es una solución.

.Net Framework nos permite comprimir archivos o informacion en memoria haciendo uso del namespace System.IO.Compression.

Este namespace contiene 2 clases que representan a los 2 algoritmos de compresion que vienen incorporados con .Net, Deflate y GZip, el codigo que se presenta mas adelante utiliza GZip.


using System;
using System.IO;
using System.IO.Compression;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public class CompressClass
{
    private byte[] LeerBytes(Stream param)
    {
        byte[] buffer = new byte[param.Length];
        using (MemoryStream ms = new MemoryStream())
        {
            int bytesRead = 0;
            do
            {
                bytesRead = param.Read(buffer, 0, buffer.Length);
                if (bytesRead > 0)
                {
                    ms.Write(buffer, 0, bytesRead);
                }
            } while (bytesRead > 0);

            return ms.ToArray();
        }
    }
    public static byte[] GetCompression(Stream param)
    {
        using (MemoryStream memoria = new MemoryStream())
        {
            using (GZipStream algoritmogzip = new GZipStream(memoria, CompressionMode.Compress, true))
            {
                algoritmogzip.Write(LeerBytes(param), 0, (int)param.Length);
            }
            return memoria.ToArray();
        }
    }
}

Como podrán ver por el código, es muy sencillo, no hay para que complicarse.

El resultado o retorno del método lo he dejado intencionalmente como un arreglo de bytes, esto para que se pueda utilizarlo como respuesta de un response en u entorno web o para que lo carguen al objeto stream de su preferencia.

Somo usar la clase
byte[] compressInfo = CompressClass.GetCompression(new StreamReader(Server.MapPath("images/piechart.tiff"), true).BaseStream);

Cambiando la respuesta de una página ASPX

martes, 26 de enero de 2010
El cambio de respuesta de una página web (en general) es una práctica bastante vieja, y muy útil a la hora de solucionar problemas de compatibilidad entre versiones de una misma plataforma o para pasar información entre plataformas.

Para poder realizar esta tarea en una página ASPX los pasos a seguir son bastante sencillos:

Se deberá eliminar el HTML del archivo mipagina.aspx (si es que estamos trabajando con code behind) y dejar tan solo la directiva de la página.

En el archivo mipagina.aspx.cs (en el page load para el caso del demo de mas adelante) es necesario pasar ciertos datos al objeto Response, éste se encarga de devolver la respuesta a la solicitud recibida por el cliente, y es en este paso que nosotros vamos a configurar esa respuesta de acuerdo a nuestras necesidades.


  • Definir el tipo de contenido usando la propiedad ContentType para tener una lista completa de los valores que podemos usar en esta propiedad se puede consultar el siguiente sitio web http://msdn.microsoft.com/en-us/library/ms775147(VS.85).aspx#Known_MimeTypes
  • Si el tipo de respuesta es en algún contenido que pueda ser desplegado en el navegador (documento de texto, imagenes, etc.) tenemos que declarar el mismo en un header (cabecera) que describe el contenido a ser desplegado y la forma de hacerlo, por razones didácticas vamos a ver 2 opciones, inline y attacch
  • Una vez configurado el response debemos escribir en el buffer lo que deseamos pasarle al cliente, esta tarea se realiza a traves de varios métodos (solo 1 a la vez) de acuerdo a nuestros requerimientos, por ejemplo en el caso de archivos binarios podemos usar el metodo BinaryWrite el cual recibe un arreglo de bytes que representan la respuesta que hemos compuesto, Write que tambien recibe un arreglo de bytes que representan lo mismo pero en este caso podemos usarlo en caso de querer pasar un recurso que no necesariamente este en memoria, por ejemplo la ruta virtual de una imagen.
  • Para finalizar debemos llamar al metodo End del Response para que finalice el envio al cliente y con esto terminamos.


Si bien el ejemplo a continuación es muy básico, nos permite ver las muchas posibilidades que esta opción nos brinda.


El Código
Archivo getImage.aspx.cs
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.ContentType = "image/jpeg";
        Response.AddHeader("Content-Disposition", "inline; filename=banner.jpg");
        Response.Write(Server.MapPath("~/images/todayscompanybanner.jpg"));
        Response.End();
    }

Archivo getImage.aspx


Otra opción de código
        using (FileStream ms = new FileStream(Server.MapPath("~/installers/20100101.zip"),FileMode.Open))
        {
            long dataLengthToRead = ms.Length;
            int blockSize = dataLengthToRead >= 5000 ? 5000 : (int)dataLengthToRead;
            byte[] buffer = new byte[dataLengthToRead];
            Response.Clear();
            Response.ClearContent();
            Response.ClearHeaders();
            Response.BufferOutput = true;
            Response.AddHeader("Content-Disposition", "attach; filename=someinstaller.zip");
            Response.ContentType = "application/octet-stream";
            while (dataLengthToRead > 0 && Response.IsClientConnected)
            {
                Int32 lengthRead = ms.Read(buffer, 0, blockSize);
                Response.OutputStream.Write(buffer, 0, lengthRead);
                Response.Flush();
                dataLengthToRead = dataLengthToRead - lengthRead;
            }
            Response.Flush();
            Response.Close();
        }
        Response.End();

Software de actualización de software

lunes, 25 de enero de 2010
Hace aproximademente unos 2 años una nueva forma de despliegue de aplicaciones viene cobrando mucha fuerza entre los distribuidores de sistemas operativos y aplicaciones que ahora esta siendo adoptada por los proveedores de hardware.

Al decir software de actualización de software no me refiero a un programa que al iniciarse busca su última versión en internet, ese fue solo el primer paso hace un tiempo atras, ahora lo que se quiere conseguir es que el usuario al iniciar su equipo de computo (cualquiera que este sea y bajo cualquier sistema operativo) este -el equipo- cuente con las últimas versiones tanto de los controladores de los dispositivos con los que cuenta como como las aplicaciones de software instaladas en él.

Hace ya un buen tiempo que este tipo de soluciones estan dando vueltas en el mundo informático, lo malo es que las soluciones propuestas solo eran usadas por entendidos en el tema y no por el usuario final (que son los mas numerosos).

Una excelente implementacion de este tipo de sistemas y hasta ahora la estrella de este nuevo tipo de aplicaciones seria iTunes del mundo de la manzana, una implementación simple, extensible, muy clara, y como ya todos sabemos muy costosa para ciertas cosas; el concepto de ésta aplicación es en si avanzado para la época en la que se lanzo, es decir, esta marcando una tendencia.

Hasta donde se puede ver, este servicio debería contar con ciertas características que son básicas para su buen funcionamiento.


  • Deberá implementarse como un servicio, claro esta, orientado al lado de "la fuerza" en el que será desplegado.
  • Este -el servicio- deberá ser liviano, rápido y con un motor de busqueda muy preciso.
  • El uso de medios estandares de comunicacion es obligatorio
  • Deberá ser extensible a traves de estandares no solo para un mismo proveedor de software sino tambien para proveedores de contenido.
  • Deberá permitir la creación de imagenes o copias de respaldo del estado del sistema en un momento dado
  • El cliente de este servicio (aplicación de cara al usuario final) deberá ser simple y ergonomica al uso que el usuario le dará (mucha información puede causar mucha confusión).


Si bien estos son solo unos puntos a ser implementados, hay varias compañias que estan presentando sus propias visiones de lo que es un servicio de estos, personalmente he podido usar el SoftPaq de HP el cual me parecio muy bueno, tiene un par de fallas en el tema de la versión de algunos paquetes pero soluciono el tema de tener que buscar los controladores de la ultima notebook que compré.

Otra compañia que apuesta al servicio es Intel con la propuesta que lanzo en el CES2010 si bien esta compañía es nueva en el area del desarrollo de software a apostado con todo y como es costumbre en productos con su sello marcará un rumbo a seguir.

A continuación presento una serie de enlaces que muestran a este tipo de servicios siendo ofrecidos (los que yo conozco). Hay muchas otras empresas que seguramente ofrecen actualizaciones de la misma forma.


Subir archivos a un servidor por partes

viernes, 8 de enero de 2010
Si bien es posible subir un archivo desde una página web usando el control FileUpload muchas veces nos vemos en la necesidad de hacerlo desde otros entornos, para esto .Net Framework cuenta con algunos assemblies que nos permiten tanto subir un archivo como una unidad o por partes.
Para representar esta situación lo que vamos a hacer es un proyecto de tipo class library (assembly),este usara los namespaces System.IO, System.Net, será necesario agregarlos a la lista de namespaces.
En esta oportunidad haremos uso de la clase WebClient (Ssytem.Net.Webclient) esta clase es muy útil en este contexto, ya que nos abstrae de la complejidad que se presenta al hacer un PUT http con otras clases del framework.
El código siguiente presenta a una función que toma un stream (el archivo a subir) y lo copia al destino en pequeñas porciones "chunks" de datos.
Algunas salvedades con respecto al código:
El servidor que use para desarrollar el código es un IIS 6.0 en el cual tengo permisos para subir archivos, si la seguridad del IIS no es ajustada a las necesidades de la funcionalidad que estamos desarrollando los errores 405 y 401 (Acceso denegado y no autorizado respectivamente) se harán presentes.
Las excepciones que están siendo capturadas son solo algunas de las que se podrían presentar, capturas mas especializadas son recomendadas.

public void Upload(string fileName, Stream localStream, int chunkSize)
{
//Declaracion de variables a ser usadas
System.Net.WebClient Client = new System.Net.WebClient();
byte[] chunk = new byte[localStream.Length];
int offset = 0;
int byteRead = 0;
//Establecer el usuario que tiene permisos para subir archivos
Client.Credentials =new System.Net.NetworkCredential("MiUsuario",
"MiClave",
"MiDominio");
try
{
do
{
//Se verifica el tamaño del bytes a leer
//para no agregar bytes al archivo
if (localStream.Length - localStream.Position <>
chunkSize = (int)(localStream.Length - localStream.Position);
//se sube una porcion del archivo a memoria
byteRead = localStream.Read(chunk, offset, chunkSize);
//se sube la porcion leida del archivo al destino
Client.UploadData("http://localhost/test/" + fileName,
"PUT", chunk);
//marcamos la posicion de la ultima lectura
offset += byteRead;
} while (localStream.Position != localStream.Length);
}
catch (FileNotFoundException ex)
{ throw new Exception("File Not Found", ex); }
catch (TimeoutException ex)
{ throw new Exception("Timeout", ex); }
catch (Exception ex)
{ throw new Exception("Unknow error", ex); }
}

Ejecución de assemblies externos al proyecto

jueves, 29 de octubre de 2009
Si bien un assembly puede ser referenciado en el proyecto en el que estamos trabajando y simplemente usarlo desde ahí, se presentara alguna oportunidad en la que tengamos que ejecutar o usar (como quieran) un asembly que no pertenece al proyecto (assembly externo) .Net 2.0 nos permite realizar este tipo de excentricidades de una forma muy sencilla. Reflection.

Se utiliza Reflection para crear instancias dinámicas de objetos que son agregados a nuestra aplicación después de haber sido compilada, esto permite extender las funcionalidades de una aplicación dada.

Supongamos lo siguiente:

Tenemos una aplicación de comercio electrónico (por ejemplo), y en el modulo de pagos (o cobros) queremos implementar algunas reglas, pero estas reglas pueden variar en el futuro; si las reglas no fueran a cambiar simplemente escribimos el código necesario y lo empaquetamos, pero como estas van a cambiar necesitamos implementar un par de pasos mas en la verificación de los datos (las reglas dinámicas).

Si bien vamos a usar una interfaz para conocer de antemano la forma que tendrán los assemblies externos esto no nos limita a usar un solo assembly con esa forma, pueden ser muchos con diferentes nombres y namespaces que hereden de nuestra interfaz, y en caso que necesitemos otra interfaz, ahí si tenemos que recompilar las modificaciones del “Motor de reglas” simplemente agregar las interfaces necesarias, el código para pasarle los parámetros al momento de la ejecución y listo.

Para poder validar o comparar con algo, el código contempla una cadena de conexión, el tema del acceso a datos no será tocado en este post ya que el tema es amplio.

Una vez que se tienen todos los datos a validar, la interfaz presenta un método Execute (Ejecutar) al que le pasamos los parámetros XML y este nos devuelve, en este caso) un valor boléano (verdadero | falso), verdadero si la ejecución se llevo a cabo sin ningún error y falso si existe algún error, en este caso la propiedad ErrorMessage contendrá el mensaje de error que describa la situación.

En el caso que necesitemos algún otro tipo de retorno, el método ExecuteConstraint retorna un objeto de tipo object en caso de hacer algún tipo de transformación para llevar el resultado a la aplicación.

Como es bien sabido, muchas cosas pueden fallar al momento de ejecutar una aplicación, es por eso que el método ExecuteConstraint viene rodeado de un try/catch con aquellas excepciones más comunes.


Sin más les presento el código.


using System;
using System.Data;
using System.Configuration;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
using System.IO;
using MiApp.AdministradorReglas.Interfaces;
//hace referencia al assembly donde se encuentran las interfaces
namespace MiApp.AdministradorReglas
{
/// <summary>
/// Esta clase fue creada con la intencion de ayudar en la ejecucion de assemblies ///externos a una aplicacion construidos basandose en la interface ///MiApp.AdministradorReglas.Interfaces
/// </summary>
/// <see cref="MiApp.AdministradorReglas.Interfaces"/>
class ConstraintHelper
{
private StringBuilder _errorMessage;
private string identity;
/// <summary>
/// Mensaje de error devuelto de la ejecucion del assembly.
/// </summary>
public string ErrorMessage
{
get { return _errorMessage.ToString(); }
set
{
_errorMessage.Remove(0, _errorMessage.Length);
_errorMessage.Append(value);
}
}
/// <summary>
/// Usado para identificar una constraint especifica.
/// </summary>
public string Identity
{
get { return identity; }

set { identity = value; }

}

/// <summary>
/// Enumeracion publica que permite distinguir los tipos de assemblies disponibles
/// </summary>
public enum enuConstraintType
{
enuApplicationType
}
public ConstraintHelper()
{
_errorMessage = new StringBuilder();
}
/// <summary>
/// Ejecuta una regla en base a sus parametros.
/// </summary>
/// <param name="enuType">Determina el tipo de interfaz a ser utilizado para la ejecucion.</param>
/// <param name="args">Un string XML con valores que seran validados por el assembly externo.</param>
/// <param name="dbConnection">Conexion a la base de datos usada para verificar los datos lmacenados.</param>
/// <param name="userID">Identifica al usuario que solicita la verificacion.</param>
/// <param name="AssemblyPath">ruta en la que se encuentra el assembly externo</param>
/// <remarks>Este metodo extrae y ejecuta el metodo Execute de un assembly externo de tal modo que se pueda usar en el contexto presente.
///<returns>Objec dependiendo del retorno de la regla a ser ejecutada</returns>
public object ExecuteConstraint(enuConstraintType enuType, System.Data.Common.DbConnection dbConnection, string userID, string AssemblyPath, String args)
{
if (args == string.Empty)
{
this._errorMessage.Append("Parametros no validos");
return null;
}
Assembly assembly;
object result = null;
try
{
// Load the requested assembly and get the requested type
assembly = Assembly.LoadFrom(AssemblyPath);
switch (enuType)
{
case enuConstraintType.enuApplicationType:
IApplicationConstraints myApplication = assembly.CreateInstance(TypeName, true) as IApplicationConstraints;
if (myApplication != null)
{
//setting up basic properties
myApplication.DatabaseConnection = dbConnection;
myApplication.UserID = userID;

//executing and returning the result
if (!myApplication.Execute(args))
{
this._errorMessage.Append(myApplication.ErrorMessage);
}
result = (IApplicationConstraints)myApplication;
}
else
{
this._errorMessage.Append("Constraint Not Available");
result = null;
}
break;
}
return result;
}
catch (TargetException ex)
{
this._errorMessage.Append(string.Format("Assembly {0} Not Load", TypeName));
throw new Exception(string.Format("Assembly {0} Not Load", TypeName), ex);
}
catch (TargetInvocationException ex)
{
this._errorMessage.Append(string.Format("{0} Method Not Invoked", TypeName));
throw new Exception(string.Format("{0} Method Not Invoked", TypeName));
}
catch (FileNotFoundException ex)
{
this._errorMessage.Append(string.Format("Assembly {0} Not Found", TypeName));
throw new Exception(string.Format("Assembly {0} Not Found", TypeName), ex);
}
catch (TypeLoadException ex)
{
this._errorMessage.Append(string.Format("Type {0} Not Load", TypeName));
throw new Exception(string.Format("Type {0} Not Load", TypeName), ex);
}
catch (Exception ex)
{
this._errorMessage.Append(ex.Message);
throw new Exception(ex.Message, ex);
}
}
}
}



El código del assembly de interfaces
using System;
using System.Text;
using System.Data;
using System.Data.Common;

namespace MiApp.AdministradorReglas.Interfaces
{
public interface IApplicationConstraints
{
bool Execute(string parameters);
string UserID
{
get;
set;
}
DbConnection DatabaseConnection
{
get;
set;
}
string UserName
{
get;
set;
}
string ErrorMessage
{
get;
set;
}
}
}


Tomar en cuenta que ambos proyectos deben ser de tipo ClassLibrary de tal forma que se pueda usar en cualquier tipo de aplicación, además el assembly de interfaces debe estar referenciado al “Motor de reglas”.

Manejo de transacciones en un procedimiento almacenado

martes, 27 de octubre de 2009
El manejo de transacciones puede ser administrado ya sea desde la aplicación, la capa de acceso a datos o la misma capa de datos, la decisión de donde realizar esta administración depende exclusivamente del arquitecto que este a cargo del proyecto en el que estamos trabajando o en su defecto del Líder técnico del mismo.

En este post lo que quiero mostrar es como se pueden manejar transacciones desde un procedimiento almacenado, es un ejemplo bastante simple pero my útil al momento de realizar muchas operaciones dentro de un mismo contexto, si algo sale mal la Base de datos no debería quedar corrupta y/o dañada por un error que puede ser atribuible a causas externas a la aplicación como caidas de la red o que el servidor simplemente no responde.



SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Andres Ureña
-- Create date: 27/10/2009
-- Description: Demo de uso de transacciones en Procedimientos almacenados
-- =============================================
CREATE PROCEDURE SPConManejoDeTransacciones
@Dato1 int,
@dato2 int,
@dato3 varchar(10)
AS
BEGIN
BEGIN TRY
/*
Iniciar la transaccion antes de comenzar con la modificacion de datos
*/
BEGIN TRANSACTION DEMOTRANSACCION
/*
Comenzamos con la modificacion de los datos
*/
INSERT INTO TABLA1(DATO1) VALUES(@DATO1)
UPDATE TABLA2 SET DATO3=@DATO3 WHERE DATO2=@DATO2
/*
si todo salio bien se hace un commit de la transaccion
*/
COMMIT TRANSACTION DEMOTRANSACCION
END TRY
BEGIN CATCH
/*
Algun error ha sucedido, es necesario devolver este con
su numero respectivo hacia la aplicacion
*/
ROLLBACK TRANSACTION DEMOTRANSACCION
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_MESSAGE() AS ErrorMessage
END CATCH
END
GO

Este procedimiento puede ser consumido de muchas formas, y desde cualquier plataforma, en este caso la sintaxis es de SQL Server 2005 y la aplicación que lo consume es una aplicación Web implementada en ASP.Net 2.0, pero podría ser utilizado desde un entorno Java, COM u otro siempre y cuando se tenga acceso a la base de datos y se tengan permisos de ejecución sobre procedimientos almacenados.

Encriptación de archivos de configuración

jueves, 16 de julio de 2009
Una de las facilidades que nos brinda .Net con respecto a la configuración de una aplicación es el uso de archivos de configuración, estos archivos son de texto plano con esquema XML; en el podemos almacenar todas las llaves, valores, cadenas, conexiones y otras cosas que necesitemos para que nuestra aplicación funcione como se planeo.

Mucha de la información que es contenida en estos archivos de configuración puede que sea muy sensible, en este caso lo ideal seria que no sea legible a simple vista, pero ¿como hacer que el valor este disponible para la aplicación pero que no sea legible?

Existe una forma (de tantas posibles) que desarrollare a continuación.

Lo que haremos es encriptar sección por sección del archivo de configuración. Para lograr este cometido tenemos que seguir una serie de pasos.

1) Declaración de Assembly de encriptación.

Tenemos que identificar en el web.config que proveedor de encriptación vamos a utilizar, en este caso vamos a usar RSAProtectedConfigurationProvider, para poder hacer esto es necesario crear esta sección en el archive de configuración


<configProtectedData defaultProvider="MiNuevoProveedor">

<providers>

<add name="MiNuevoProveedor"

type="System.Configuration.RsaProtectedConfigurationProvider,

System.Configuration,

Version=2.0.0.0,

Culture=neutral,

PublicKeyToken=b03f5f7f11d50a3a,

processorArchitecture=MSIL"

keyContainerName="ContenedorDeLlavves"

useMachineContainer="true" />

</providers>

</configProtectedData>


En este caso la instancia de la clase de encriptación es “ContenedorDeLlaves” y el proveedor es “MiNuevoProveedor”

2) El Segundo paso consiste en el uso de una herramienta de consola, esta es la Herramienta de registro de Information Services (aspnet_regiis.exe), con esta herramienta vamos a crear las llaves de seguridad en el servidor donde la aplicación se encuentra. Esta herramienta se encuentra en la ruta de instalación de la versión 2.0 del framework (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727). Para crear este contenedor en necesario usar la ventana de comandos del sistema operativo con el siguiente comando:


aspnet_regiis -pc "ContenedorDeLlaves" –exp


El argumento –PC crea un contenedor en un espacio aislado del registro del sistema para contener la llave privada del algoritmo de encriptacion, el argumento –exp permite hacer exportable este contenedor en caso que estemos trabajando en una granja de servidores.

3) Ahora que ya tenemos el contenedor, debemos establecer quien tendrá acceso a este, para esto usamos esta línea de comando:

Para Windows XP.


aspnet_regiis -pa " ContenedorDeLlaves " "NombreDePC\ASPNET"


Para Windows 2003.


aspnet_regiis -pa " ContenedorDeLlaves " "NT AUTHORITY\NETWORK SERVICE"


La diferencia entre sistemas operativos se debe a que el servicio que se encarga del acceso a sitios Web es diferente de S.O. a S.O.


4) Antes de poder usar el servicio de encriptación del proveedor, necesitamos crear una referencia a este ensamblado en la sección de ensamblados del archive de configuración de la siguiente forma

<compilation debug="false">

<assemblies>

<add assembly="System.Configuration,

Version=2.0.0.0,

Culture=neutral,

PublicKeyToken=b03f5f7f11d50a3a" />

</assemblies>

</compilation>

5) Ahora ya podemos encriptar, como se comento al principio la idea es cifrar una sección entera del archive de configuración, en este caso elegimos las cadenas de conexión (connectionStrings)


aspnet_regiis -pef "connectionStrings" "C:\Intepub\wwwroot\Test" -prov " MiNuevoProveedor "


El atributo –pef nos permite encriptar la seccion descrita a continuacion del mismo, si deseamos desencriptar el comando deberá ser –pdf en vez de -pef. Este atributo recibe 2 parámetros, el primero es la sección que se desea cifrar/descifrar y el segundo es la ruta donde se encuentra el archivo de configuración. El atributo –prov especifica el proveedor de encriptación que será utilizado para el cifrado (en este caso el especificado en el paso 1)


En el caso que nos encontremos en un escenario Web farm el contenedor de la llave puede ser exportado e importado en cada uno de los miembros del Web farm con las siguientes líneas de comando.


Para exportar


aspnet_regiis -px " ContenedorDeLlaves " "C:\ ContenedorDeLlaves.xml" -pri


El atributo –px recibe 2 argumentos, el primero es el nombre del contenedor de claves a ser exportado y el Segundo es la ruta donde se deberá crear el archive XML con la información del mismo.

El atributo –pri establece si la llave primaria también debe ser exportada.


Para Importar

aspnet_regiis -pi " ContenedorDeLlaves " "C:\ ContenedorDeLlaves.xml"


Copiar el archive XML resultante de la exportación al Nuevo servidor, abrir la ventana de comandos y utilizar el comando de importación, el atributo –pi recibe 2 argumentos, el primero es el nombre del contenedor a ser importado y el Segundo es la ruta física del archive XML resultante de la exportación.


Ahora que la sección connectionString esta cifrada la información esta segura, no es necesario cambiar el código de la aplicación para que lea esta sección o cualquier otra que haya sedo cifrada ya que el mismo CLR se encargara de esa tarea con la información proporcionada en el contenedor para el caso de los usuarios que pueden acceder al servicio, como el proveedor y llaves de encriptación para obtener la información solicitada.


Nota:

Todos los comandos deberán ser ejecutados bajo una cuenta con privilegios administrativos.

Creando instaladores para sitios Web

martes, 14 de julio de 2009

Desde hace ya varias versiones, Visual Studio trae un tipo de proyecto que nos facilita las tareas de despliegue de soluciones, y ya en las últimas versiones ha venido ofreciendo una herramienta realmente confiable y de fácil implementación.

Los proyectos de Setup & Deployment se diferencian del resto por que no se debe escribir ni una sola línea de código para que funcionen, es mas si se utiliza el asistente para incrustar uno de estos proyecto en nuestra solución no necesitamos mas para obtener como resultado un instalador (básico) para un despliegue indoloro (depende del grado de complicación al que hayamos llegado).

Como soy un WebDeveloper el presente post trata (como lo dice el título) sobre instaladores para sitios web, si bien un sitio web no se lo instala en todas partes como una aplicación de escritorio o mobile, en el caso de trabajar con clientes externos a nuestro entorno es necesario (lo dicen las buenas prácticas) presentar un producto bien terminado, empaquetado y listo para la distribución. Comencemos entonces.

New Project

Voy a suponer que ya contamos con una solución web lista y funcionando, vamos a agregar un proyecto mas de tipo setup & deployment y como veran tenemos varias opciones, para hacer el post mas didáctico vamos a ir por el Setup Wizard, este proyecto nos tomara como proyecto principal a aquel proyecto que tengamos como start up de la solución, en el caso de proyectos web solo podemos incluir el Content de este, aquí vienen todos los assemblies que neesita para funciona asi que no es necesario incluir el resto de proyectos (librerías) al instalador.

Project Output

Una vez ubicado el proyecto en el directorio correspondiente llegamos a la vista del file system del instalador, veamos sus partes.

File System

En lugar de tener un espacio de edición de código, tenemos una especie de explorador, a la izquierda se ven las carpetas o folders que serán desplegados en la máquina destino (target) y a la derecha la lista de archivos, assemblies, project outputs y demas recursos que necesite el instalador para cumplir su misión, para poder agregar algo al file system solo necesitamos hacer click con el boton derecho sobre la zona de archivos y seleccionar la opción que se adecue a nuestras necesidades. (entre los recursos que normalmente se adjuntan estan los documentos de licencias, imagenes de splash, banners y assemblies que no van dentro de la solución pero que son necesarias).

Contando solo con esto ya tenemos un instalador de nuestro proyecto, pero, como todos sabemos una web que no se conecte a una base de datos no es web (punto de vista personal) entonces lo que faltaría aquí es establecer la conexión a la base de datos. ¿Y qué pasa si la base de datos no existe? bueno hay que crearla, desde la instalación el usuario final debería ser capaz de realizar todas estas tareas (y muchas otras más) sin complicarse la vida, es mas un usuario final con 0(cero) conocimiento de desarrollo de software debería ser capaz de instalar nuestro producto así como instala un juego o una aplicación de escritorio.

Obviamente una aplicación web no se parece mucho a una de escritorio, depende de la existencia de IIS y de los permisos necesarios para acceder a este. Es bueno saber que un setup de Windows Installer es capaz de detectar aquellos recursos que sean necesarios para el producto que se esta instalando, en este caso IIS, si este servicio no esta presente el instalador se encarga de levantar una excepción informandole al usuario final la situación.

Volviendo al Demo, necesitamos crear la base de datos a la que nuestra web se conectara y ademas tenemos que modificar el archivo web.config para indicar que la cadena de conexión apunta al servidor del cliente con el usuario que este ha definido. Para lograr esto hay que escribir código. :)

Existe una clase que permite que sobrecarguemos los procesos que se llevan a cabo cuando una aplicación (en este caso web) se está instalando, esta es la clase installer (System.Configuration.Install.Installer)

Como aprovechar la clase Installer

La idea de esta clase es la de proporcionar un vínculo entre el proceso de instalación y los eventos que esta genera con las modificaciones y/o configuraciones que necesita nuestra solución para estar correctamente instalada, esta clase está disponible sin importar el tipo de proyecto que se este trabajando (web o escritorio) pero para el caso del demo vamos a trabajar con Web.

Para comenzar, la clase Installer debe ser heredada, y esta clase derivada deberá encontrarse en un componente que este incluido en el proyecto de instalacion, las cosas deberán verse de esta forma:

SolutionExplorer

Como veran el proyecto CustomInstaller solo tiene una clase (CustomInstaller.cs) que es derivada de la clase Installer, este proyecto es de tipo ClassLibrary y el output del mismo debera estar incluido en el proyecto de instalación.

El namespace System.Configuration.Install debe estar referenciado en el proyecto CustomInstaller para poder tener dicponible la clase Installer.

Existen procesos que pueden ser sobrecargados al heredar esta clase, Install, Commit, Rollback y Unistall, para cada uno de ellos podemos escribir el código que necesitemos, en el caso de este demo solo vamos a usar el proceso Install. El código debería verse mas o menos asi:

using System;
using System.Configuration;
using System.Configuration.Install;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Web.Configuration;
using System.DirectoryServices;
using System.Data;
using System.Data.SqlClient;
using System.Text;

namespace CustomInstaller
{
[RunInstaller(true)]
public class CustomInstaller : Installer
{
const string DATABASE_CONNECTION_KEY = "ConnectionStringDemo";
public override void Commit(System.Collections.IDictionary savedState)
{
base.Commit(savedState);
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string targetSite = string.Empty;
string targetVDir = string.Empty;
string targetDirectory = string.Empty;
string Server = string.Empty;
string Database = string.Empty;
string Username = string.Empty;
string Password = string.Empty;

try
{
//obtiene la informacion pasada por el usuario
targetSite = Context.Parameters["targetsite"];
targetVDir = Context.Parameters["targetvdir"];
targetDirectory = Context.Parameters["targetdir"];
//'Conexion a DB
Server = Context.Parameters["Server"];
Database = Context.Parameters["Catalogo"];
Username = Context.Parameters["Username"];
Password = Context.Parameters["Password"];
}
catch(Exception ex)
{
throw new Exception("Error al acceder a los valores de contexto :: " , ex);
}
InstallDatabase(targetSite, targetVDir, Server, Database, Username, Password);
}
public override void Rollback(System.Collections.IDictionary savedState)
{
base.Rollback(savedState);
}
public override void Uninstall(System.Collections.IDictionary savedState)
{
base.Uninstall(savedState);
}
private void InstallDatabase(string targetSite, string targetVDir, string server, string database, string Username, string Password)
{
string con = string.Empty;
SqlCommand cmd = new SqlCommand();
try
{
//Creando la base de datos con el nombre proporcionado por el usuario
con = string.Format("server={0};database=master;UID={1};PWD={2}", server, Username, Password);
cmd.CommandText = string.Format("create database {0}",database);
cmd.CommandType = CommandType.Text;
cmd.Connection = new SqlConnection(con);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}
catch (Exception ex)
{
throw new Exception(string.Format("La base de datos {0} no pudo ser creada ",database), ex);
}
try
{
//Creando la base de datos (contenido)
con = string.Format("server={0};database={3};UID={1};PWD={2}", server, Username, Password, database);
StreamReader script = new StreamReader(@"C:\inetpub\wwwroot\" + targetVDir + @"\DatabaseScript.sql");
cmd.CommandText = script.ReadToEnd();
cmd.CommandType = CommandType.Text;
cmd.Connection = new SqlConnection(con);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw new Exception("
Los datos de la base de datos no fueron creados :: " , ex);
}
finally
{
if (cmd.Connection.State == ConnectionState.Open)
{ cmd.Connection.Close(); }
}

try
{
//Abriendo el archivo web.config del sitio web
Configuration config = WebConfigurationManager.OpenWebConfiguration("
/" + targetVDir);

//Creando una nueva llave en la seccion de connectionStrings para nuestra base de datos
ConnectionStringSettings appDatabase = new ConnectionStringSettings();
appDatabase.Name = DATABASE_CONNECTION_KEY;
appDatabase.ConnectionString = con;
appDatabase.ProviderName = "
System.Data.SqlClient";

config.ConnectionStrings.ConnectionStrings.Clear();
config.ConnectionStrings.ConnectionStrings.Add(appDatabase);

//grabando las modificaciones
config.Save();
}
catch (Exception ex)
{
throw new Exception("
El archivo web.config no pudo ser modificado :: " , ex);
}
}
}
}
 

Nota

El atributo [RunInstaller(true)] debe estar presente, de lo contrario la clase no se instancia y por ende el código no se ejecuta.

Interfaz de usuario del asistente

Como se puede ver, no es nada complejo, ahora lo que necesitamos es pasarle los parametros de contexto desde la interfaz (UI) del asistente de instalación, para eso vamos a adicionar pasos al instalador.

Primero vamos a familiarizarnos con el IDE y como agregamos estos pasos al asistente, para ver el UI del mismo tenemos 2 opciones, hacer click con el boton derecho sobre el proyecto de instalación en la opción ver (view), seleccionar Interfaz de usuario (User Interface)

image

Otra opción es usando los botones en la ventana del explorador de la solución al seleccionar el proyecto de instalación. (el tercero de derecha a izquierda)

image

La IDE que se desplegará debiera verse mas o menos asi:

image

En la imagen se puede ver que ya tengo agregado un paso con el nombre Texboxes (A) y que en la ventana de propiedades se ven todas las propiedades que se tienen disponibles. Como verán (o mejor dicho como no verán) no hay forma de ver la interfaz de forma gráfica como un formulario windows, debemos trabajar todo a traves de estas propiedades; todas las propiedades que terminen con "label" son las etiquetas que mostraran una descripción del campo que se solicita al usuario, las que terminan con el termino "property" son el nombre con el que seran administradas las propiedades desde la clase Installer (Context) (eso se puede ver en el código de la clase derivada de Installer)

El nombre de las propiedades sera siempre en mayúsculas (automaticamente) y debe ser exactamente el mismo cuando accedamos a ellas desde la información de contexto.

Deben preguntarse ¿Por qué hay dos grupos, Install y Administrative Install? (bueno si no se lo preguntaban antes ahora sí) la razón es sencilla, el grupo Install es el que se despliega al usar el instalador como usuario final, es decir, que el usuario inicie el proceso de instalacion al ejecutar el setup.exe, el otro grupo (Administrative Install) se ejecuta cuando un administrador de red ejecuta el instalador desde un distribuidor de aplicaciones (SMS, MOS, etc.) el demo no va a tocar ese tema (aun) asi que lo dejamos como se ve e la imagen.

En este caso solo he agregado 1 paso más al instalador, se pueden agregar cuantos pasos sean necesarios para obtener toda la información requerida por el instalador para que todo salga bien, en el caso de este demo solo se solicita la información necesaria para la creación de la base de datos a la que nuestra aplicación web se va a conectar (servidor, base de datos, usuario y clave).

Con todos estos pasos (que no son muchos) es posible crear un instalador que permitirá no solo instalar sino que reparar y desinstalar nuestra aplicación en el cliente sin complicarle la vida.

Como consumir un Worflow desde .Net y desde fuera de .Net

jueves, 23 de abril de 2009

Ahora que el WWF esta disponible, tenemos mucha informacion de que es WWF pero en muy pocos lugares(sobre todo en español) no hay información de cómo consumir un workflow ya sea desde un entorno.Net como fuera de el, para ir directo al grano les mostrare un  poco de código que es el resultado de pelear con el WWF por unos días.

Primero vamos a crearnos un WF muy sencillo, un “hola mundo”.

inicio -->CodeActivity1(Comenzando)-->CodeActivity2(Medio)-->CodeActivity(Finalizando)-->end

El código que viene detrás de este WF es este:

Public class Workflow1

    Inherits SequentialWorkflowActivity

 

    Private Sub Comenzando(ByVal sender As System.Object, ByVal e As System.EventArgs)

        System.Diagnostics.Debug.WriteLine("Comanzando el Workflow")

    End Sub

 

    Private Sub Medio(ByVal sender As System.Object, ByVal e As System.EventArgs)

        System.Diagnostics.Debug.WriteLine("A Medio Workflow")

    End Sub

 

    Private Sub Finalizando(ByVal sender As System.Object, ByVal e As System.EventArgs)

        System.Diagnostics.Debug.WriteLine("Finalizando el Workflow")

    End Sub

End Class

 

Como verán nada del otro mundo, ahora para consumir este WF debemos crear un objeto de tipo WorkflowRuntime al que le pasaremos como parámetro en un objeto Type el WorkFlow que queremos ejecutar de la siguiente manera.

Para comenzar definir un objeto runtime de manera shared para toda la clase

Public Shared wr As New System.Workflow.Runtime.WorkflowRuntime

 

Luego el codigo para iniciar el workflow seria el siguiente:

 

        Dim t As Type = GetType(WFLibrary.Workflow1)

        Dim instance As System.Workflow.Runtime.WorkflowInstance = wr.CreateWorkflow(t)

        instance.Start()

 

ahora si necesitan pasarle parametros al momento de crear la instancia le pueden pasar un objeto Dictionary ademas del type con lo que se necesite.

Ahora, este ejemplo funciona bien desde un entorno .Net, pero que pasa si queremos usar el WF desde un entorno COM? Facil, creamos una clase que nos sirva de puente entre COM y .Net, y esta clase se la crea en .Net de tal manera que nos sea util en cualquier parte.

Para poder realizar tal azaña solo se necesita compilar un assembly con las propiedades COMVisible, ademas de unas pocas cosas mas en el codigo.

Para empezar veamos el codigo; necesitamos referenciar un namespace de .net de manera que tengamos disponibles algunas propiedades mas al momento de crear la estructura de la clase.

Imports System.Runtime.InteropServices

Luego creamos nuestra clase especificando el atributo COMVisible en verdadero para las clases que queremos que sean visibles desde COM.

 

<ComVisible(True)> _

Public Class MyInteropClass

    Public Property MyProperty() As String

        Get

            Return "Hola Mundo...!"

        End Get

        Set(ByVal value As String)

 

        End Set

    End Property

 

End Class

 

Con esto estamos casi listos, ahora necesitamos decirle al compilador de .net que haga esta clase visible en un entorno COM, esto es para que se creen todas las entradas de registro del sistema para nuestra libreria(dll)

Menu Project->Properties->Compile

              Seleccionar Register for COM Interop

Las pruebas fueron realizadas en vb.net y en c# en un entorno en ingles, y se comprobo que en un vs 2005 en español al momento de registrar la librería no se crea la llave de registro CodeBase en donde se almacena la ruta del dll, si ese fuese el caso hay 2 soluciones, crear la llave de registro o copiar el assembly a la carpeta System32 de Windows.

Bueno con todo esto lo unico que falta es unir la clase COMVisible con el proyecto ya sea web o desktop.