Mostrando las entradas con la etiqueta programacion. Mostrar todas las entradas
Mostrando las entradas con la etiqueta programacion. 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/ 








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();

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.

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.

Encriptacion con VB.Net (básico)

Este post es para mostrar algo de código, como se puede usar el namespace de cryptography para encriptar un mensaje y volverlo a su estado original.

Para este ejemplo he usado DES(Data encription Standard) que es uno de los mas básicos, pero asi como implemente este se puede cambiar a cualquiera de los métodos simetricos de encriptación que vienen con .Net

Para ver mas informacion sobre Criptografia en .Net ver:System.Security.Cryptography

El código es el siguiente:

 

    Imports System.Security.Cryptography
Imports System.IO
''' <summary>
''' permite encriptar un mensaje haciendo uso de uno de los servicios de encriptacion mas básicos
''' </summary>
''' <param name="message">Mensaje a encriptar</param>
''' <param name="key">Llave de encriptacion</param>
''' <param name="iv">Vector de inicializacion</param>
''' <returns>Coleecion de bytes que representan el mensaje encriptado</returns>
''' <remarks>Si bien este código hace uso del metodo DES sobrecargar este con los
''' demas metodos simetricos no implica mayor complejidad</remarks>
Private Function Encrypt(ByVal message() As Byte, ByVal key() As Byte, ByVal iv() As Byte) As Byte()
Dim des As DES = New DESCryptoServiceProvider
Dim crypto As ICryptoTransform = des.CreateEncryptor(key, iv)
Dim cipherstream As New MemoryStream()
Dim cryptostream As New CryptoStream(cipherstream, crypto, CryptoStreamMode.Write)
cryptostream.Write(message, 0, message.Length)
cryptostream.Close()
Return cipherstream.ToArray()

End Function
''' <summary>
''' permite desencriptar un mensaje haciendo uso de uno de los servicios de encriptacion mas básicos
''' </summary>
''' <param name="cypherText">Mensaje Encriptado</param>
''' <param name="iv">Vector de inicializacion</param>
''' <param name="key">Llave de encriptacion</param>
''' <remarks></remarks>
Private Function Decrypt(ByVal cypherText() As Byte, ByVal iv() As Byte, ByVal key() As Byte) As String
Dim des As DES = New DESCryptoServiceProvider()
Dim crypto As ICryptoTransform = des.CreateDecryptor(key, iv)
Dim cipherStream As New MemoryStream(cypherText)
Dim cryptostrm As New CryptoStream(cipherStream, crypto, CryptoStreamMode.Read)
Dim message As String
message = New StreamReader(cryptostrm).ReadToEnd()
Return message
End Function

sub main()
'Usando los métodos creados con un archivo de texto
Dim result() As Byte
Dim algo As New FileStream("prueba.txt", FileMode.Open)
Dim mifile(algo.Length) As Byte
Dim mikey() As Byte = BitConverter.GetBytes(123465789123456789)
Dim miiv() As Byte = BitConverter.GetBytes(987654321987654321)
algo.Read(mifile, 0, algo.Length)
result = Encrypt(mifile, mikey, miiv)
Console.WriteLine("en encrypto")
Console.WriteLine(BitConverter.ToString(result))
Console.Read()
Console.WriteLine(Decrypt(result, miiv, mikey))
Console.Read()
end sub

Almacenamiento aislado

Entiendase por almacenamiento aislado a la porsión de información (que puede servir como datos de configuración por ejemplo) que podemos almacenar de forma segura en la máquina del cliente (solo para aplicaciones de escritorio), en el ejemplo de código a continuación veran que solo almaceno un string, es demostrativo, se podría guardar tanta información como sea necesaria.

 

Imports System.IO.IsolatedStorage
Imports System.IO

''' <summary>
''' Permite inicializar un archivo de almacenamiento aislado
''' para la aplicacion
''' </summary>
''' <remarks>si el archivo de almacenamiento ya existe sera
''' actualizado con la nueva información</remarks>
Private Sub SetIsolated()
Dim isoFile As IsolatedStorageFile = IsolatedStorageFile.GetMachineStoreForAssembly()
Dim isoStream As IsolatedStorageFileStream = New IsolatedStorageFileStream("sistem.dat", FileMode.OpenOrCreate, isoFile)
Dim writer As New StreamWriter(isoStream)
writer.WriteLine("esta es la configuracion almacenada")
writer.Close()
End Sub
''' <summary>
''' Permite obtener la información almacenada para este assembly de forma aislada
''' </summary>
''' <remarks>para poder leer el archivo debemos estar seguros que ya existe</remarks>
Private Sub GetIsolated()
Dim isoFile As IsolatedStorageFile = IsolatedStorageFile.GetMachineStoreForAssembly()
Dim isoStream As IsolatedStorageFileStream = New IsolatedStorageFileStream("sistem.dat", FileMode.Open, isoFile)
Dim result As String = New StreamReader(isoStream).ReadToEnd()
Console.Write(result)
End Sub


sub main()
'almacenamiento aislado
SetIsolated()
GetIsolated()
console.read()
end sub

Actividad de envio de correo electrónico para Workflow Foundation

viernes, 10 de octubre de 2008

Esta idea tal vez ya paso por la cabeza de alguien mas, no es ninguna novedad, pero es bueno compartir este tipo de "componentes" que nos facilitan la vida al momento de encarar un proyecto.

En el caso especifico de este código, se ha creado una actividad que esta orientada a ser usada con Window Workflow Foundation para el envio de correos electronicos, si bien esta clase es basica, es muy util (asi lo veo yo) y el código puede llegar a crecer de acuerdo a las necesidades propias de su implementación.

Los namespaces necesarios para el proyecto son:

Sin mas preambulo aquí les dejo el código.

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;

namespace Workflows.Activities
{
    public partial class EnviarMail: System.Workflow.ComponentModel.CompositeActivity
  
{
        public EnviarMail()
        {
            InitializeComponent();
        }

        [Description("El campo A(dirección destino) del correo electrónico")]
        [Category("AddressTo")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string AddressTo
        {
            get{ return(string)GetValue(AddressToProperty); }
            set{ SetValue(AddressToProperty, value); }
        }

        // DependencyProperty para AddressTo.
      
public static readonly DependencyProperty AddressToProperty = DependencyProperty.Register("AddressTo", typeof(string), typeof(EnviarMail));

        [Description("El campo De(dirección origen) del correo electrónico")]
        [Category("AddressFrom")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string AddressFrom
        {
            get{ return(string)GetValue(AddressFromProperty); }
            set{ SetValue(AddressFromProperty, value); }
        }

        //DependencyProperty para AddressFrom
      
public static readonly DependencyProperty AddressFromProperty = DependencyProperty.Register("AddressFrom", typeof(string), typeof(EnviarMail));


        [Description("El campo asunto del correo electrónico")]
        [Category("Subject")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string Subject
        {
            get{ return(string)GetValue(SubjectProperty); }
            set{ SetValue(SubjectProperty, value); }
        }

        //DependencyProperty para Subject.
      
public static readonly DependencyProperty SubjectProperty = DependencyProperty.Register("Subject", typeof(string), typeof(EnviarMail));


        [Description("El Cuerpo del mensaje")]
        [Category("MailMessage")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string MailMessage
        {
            get{ return(string)GetValue(MailMessageProperty); }
            set{ SetValue(MailMessageProperty, value); }
        }

        //DependencyProperty para MailMessage.
      
public static readonly DependencyProperty MailMessageProperty = DependencyProperty.Register("MailMessage", typeof(string), typeof(EnviarMail));


        [Description("Servidor para envio de correo electronico")]
        [Category("SmtpServer")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string SmtpServer
        {
            get{ return(string)GetValue(SmtpServerProperty); }
            set{ SetValue(SmtpServerProperty, value); }
        }

        //DependencyProperty para SmtpServer.
      
public static readonly DependencyProperty SmtpServerProperty =
            DependencyProperty.Register("SmtpServer", typeof(string), typeof(EnviarMail));

        [Description("Puerto por el que se esta presente el servicio de smtp")]
        [Category("SmptPort")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int SmtpPort
        {
            get{ return(int)GetValue(SmtpPortProperty); }
            set{ SetValue(SmtpPortProperty, value); }
        }

        //DependencyProperty para SmtpPort.
      
public static readonly DependencyProperty SmtpPortProperty =
            DependencyProperty.Register("SmtpPort", typeof(int), typeof(EnviarMail));

        [Description("Nombre de usuario para acceder al servidor de SMTP")]
        [Category("Username")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string UserName
        {
            get{ return(string)GetValue(UserNameProperty); }
            set{ SetValue(UserNameProperty, value); }
        }

        //DependencyProperty para UserName.
      
public static readonly DependencyProperty UserNameProperty =
            DependencyProperty.Register("UserName", typeof(string), typeof(EnviarMail));


        [Description("Password del usuario para el envio de correo electronico")]
        [Category("Password")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string Password
        {
            get{ return(string)GetValue(PasswordProperty); }
            set{ SetValue(PasswordProperty, value); }
        }

        // DependencyProperty para Password.
      
public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.Register("Password", typeof(string), typeof(EnviarMail));


        [Description("Bandera que determina si se debera proveeder de credenciales para el envio de mails")]
        [Category("UseSSL")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public bool UseSSL
        {
            get{ return(bool)GetValue(UseSSLProperty); }
            set{ SetValue(UseSSLProperty, value); }
        }

        //DependencyProperty para UseSSL.
      
public static readonly DependencyProperty UseSSLProperty =
            DependencyProperty.Register("UseSSL", typeof(bool), typeof(EnviarMail));

        protected override ActivityExecutionStatus Execute(ActivityExecutionContextcontext)
        {
            try
          
{
                System.Net.Mail.MailMessage mimail = newSystem.Net.Mail.MailMessage(AddressFrom, AddressTo, Subject, MailMessage);
                System.Net.Mail.SmtpClient mismtp = newSystem.Net.Mail.SmtpClient();
                mismtp.Host = SmtpServer;
                mismtp.Port = SmtpPort;
                mismtp.EnableSsl = UseSSL;
                if(UseSSL)
                { mismtp.Credentials = new System.Net.NetworkCredential(UserName,Password); }
               
                mismtp.Send(mimail);
                returnActivityExecutionStatus.Closed;
            }
            catch(System.Net.Mail.SmtpExceptionsmtpEx)
            {
                return HandleFault(context, smtpEx);
            }
        }
        protected override ActivityExecutionStatus HandleFault(ActivityExecutionContextexecutionContext, Exceptionexception)
        {
            return base.HandleFault(executionContext, exception);
        }

    }
}