Blog alskare

.Net y lo que surja

Archive for the ‘NetFramework’ Category

C#. Generador de Passwords aleatorios

Posted by alskare en 13/10/2009

Estoy más que seguro que cualquiera de nosotros hemos sido usuarios en cualquier ocasión de algún tipo de herramienta similar a la que intento crear en estos momentos. De hecho, con una búsqueda bastante genérica en Google surgen multitud de rutinas en cualquier lenguaje. No obstante, como soy de aquellas personas que prefiere crear sus propias rutinas antes de hacer un “Copia y Pega” de lo que han hecho otros, me dispongo a la creación de una utilidad que me permita obtener contraseñas de una manera aleatoria.

¿Qué necesito?

En principio, sólo necesito una rutina que me permita generar un password de 8 caracteres, y que incluya, al menos, un número, un carácter no alfanumérico y algún carácter en mayúscula. Podemos apreciar enseguida que, aunque la necesidad inminente es bastante sencilla, lo mejor es poder parametrizar estos elementos a los que podemos llamar “Caracteres especiales”:

  • Números
  • Mayúsculas
  • Símbolos

Debo reconocer que, a la hora de parametrizar valores, soy un amante de los porcentajes y, en este principio, baso muchas de las rutinas creadas. Es decir, si genero una rutina que me permita crear una contraseña de 10 caracteres, prefiero decir que tenga un 20% de números, un 20% de mayúsculas y otro 20% de símbolos para que la contraseña tenga: 2 números + 2 letras mayúsculas + 2 símbolos + 4 caracteres normales en minúscula. De esta manera, si tengo que ampliar la longitud de la contraseña a 20 caracteres, automáticamente cambiará el número de caracteres especiales, manteniendo la proporción.

Otro de los puntos a tener en cuenta es que los caracteres que pueden emplearse no siempre serán válidos en todos los entornos, por tanto, aunque no me he preocupado de parametrizar este punto, sí que he tenido la precaución de “separarlo” por si alguien tiene que añadir/eliminar algún carácter no compatible, por ejemplo, con un determinado servidor de correo.

string caracteres = "abcdefghijklmnopqrstuvwxyz";
string numeros = "0123456789";
string simbolos = "%$#@+-=&";

¿Cómo enfoco el resultado?

En mi caso, con contraseñas de 8 caracteres suelo pasar. De hecho, debo reconocer que la necesidad de la creación de una rutina que cree passwords automáticamente viene dada por una aplicación Web que tengo que crear en la que, cuando un usuario pierde una contraseña, se le enviará por e-correo y, la primera vez que entre, tendrá que cambiarla, así que prima más la “estética” de la contraseña que la seguridad. Lo que pasa es que, a la gente le gusta más ver símbolos raros que cualquier nombre.

Pues eso, que la idea inicial es la de crear un constructor estándar con 8 caracteres, de los cuales tendrá un 20% de caracteres especiales. Otro constructor que permita crear una contraseña con una longitud variable y otro que permita definir todo:

public GeneradorPassword()
{ }

public GeneradorPassword(int longitudCaracteres)
{ }

public GeneradorPassword(int longitudCaracteres, 
                         int porcentajeMayusculas, 
                         int porcentajeSimbolos, 
                         int porcentajeNumeros)
{ }

En cuanto al sistema de crear la rutina, no me complico tampoco la vida:

  1. Creo una cadena con la longitud de la contraseña todo en minúscula
  2. Obtengo el número de caracteres especiales a añadir
  3. Obtengo una posición aleatoria para colocar los caracteres especiales
  4. Reemplazo los caracteres especiales en las posiciones obtenidas.

El resultado

Dejo la rutina tal cuál me ha quedado:

using System;
using System.Collections.Generic;
using System.Text;

namespace jnSoftware.Utiles
{
    /// <summary>
    /// Clase que permite la generación de una contraseña. 
    /// La contraseña contiene un número de caracteres fijos y permite especificar el porcentaje
    /// de caracteres en mayúsculas y símbolos que se quieren obtener
    /// </summary>
    public class GeneradorPassword
    {


        /// <summary>
        /// Enumeración que permite conocer el tipo de juego de carácteres a emplear
        /// para cada carácter
        /// </summary>
        private enum TipoCaracterEnum { Minuscula, Mayuscula, Simbolo, Numero }

        #region Campos

        private int porcentajeMayusculas;
        private int porcentajeSimbolos;
        private int porcentajeNumeros;
        Random semilla;

        // Caracteres que pueden emplearse en la contraseña
        string caracteres = "abcdefghijklmnopqrstuvwxyz";
        string numeros = "0123456789";
        string simbolos = "%$#@+-=&";

        // Cadena que contiene el password generado
        private StringBuilder password;

        #endregion



        #region Propiedades

        /// <summary>
        /// Obtiene o establece la longitud en carácteres de la contraseña a obtener
        /// </summary>
        public int LongitudPassword { get; set; }

        /// <summary>
        /// Obtiene o establece el porcentaje de carácteres en mayúsculas que 
        /// contendrá la contraseña
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un valor que no coincida con un porcentaje</exception>
        public int PorcentajeMayusculas
        {
            get { return porcentajeMayusculas; }
            set
            {
                if (value < 0 || value > 100)
                    throw new ArgumentOutOfRangeException("El porcentaje es un número entre 0 y 100");
                porcentajeMayusculas = value;
            }
        }


        /// <summary>
        /// Obtiene o establece el porcentaje de símbolos que contendrá la contraseña
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un valor que no coincida con un porcentaje</exception>
        public int PorcentajeSimbolos
        {
            get { return porcentajeSimbolos; }
            set
            {
                if (value < 0 || value > 100)
                    throw new ArgumentOutOfRangeException("El porcentaje es un número entre 0 y 100");
                porcentajeSimbolos = value;
            }
        }

        /// <summary>
        /// Obtiene o establece el número de caracteres numéricos que contendrá la contraseña
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un valor que no coincida con un porcentaje</exception>
        public int PorcentajeNumeros
        {
            get { return porcentajeNumeros; }
            set
            {
                if (value < 0 || value > 100)
                    throw new ArgumentOutOfRangeException("El porcentaje es un número entre 0 y 100");
                porcentajeNumeros = value;
            }
        }

        #endregion


        #region Constructores
        /// <summary>
        /// Constructor. La contraseña tendrá 8 caracteres, incluyendo una letra mayúscula, 
        /// un número y un símbolo
        /// </summary>
        public GeneradorPassword()
            : this(8)
        { }


        /// <summary>
        /// Constructor. La contraseña tendrá un 20% de caracteres en mayúsculas y otro tanto de 
        /// símbolos
        /// </summary>
        /// <param name="longitudCaracteres">Longitud en carácteres de la contraseña a obtener</param>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un porcentaje de caracteres especiales mayor de 100</exception>
        public GeneradorPassword(int longitudCaracteres)
            : this(longitudCaracteres, 20, 20, 20)
        { }


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="longitudCaracteres">Longitud en carácteres de la contraseña a obtener</param>
        /// <param name="porcentajeMayusculas">Porcentaje a aplicar de caracteres en mayúscula</param>
        /// <param name="porcentajeSimbolos">Porcenta a aplicar de símbolos</param>
        /// <param name="porcentajeNumeros">Porcentaje de caracteres numéricos</param>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un porcentaje de caracteres especiales mayor de 100</exception>
        public GeneradorPassword(int longitudCaracteres, int porcentajeMayusculas, int porcentajeSimbolos, int porcentajeNumeros)
        {
            LongitudPassword = longitudCaracteres;
            PorcentajeMayusculas = porcentajeMayusculas;
            PorcentajeSimbolos = porcentajeSimbolos;
            PorcentajeNumeros = porcentajeNumeros;

            if (PorcentajeMayusculas + porcentajeSimbolos + PorcentajeNumeros > 100)
                throw new ArgumentOutOfRangeException(
                "La suma de los porcentajes de caracteres especiales no puede superar el " +
                "100%, es decir, no puede ser superior a la longitud de la contraseña");
            semilla = new Random(DateTime.Now.Millisecond);
        }

        #endregion


        #region Métodos públicos

        /// <summary>
        /// Obtiene el password
        /// </summary>
        /// <returns></returns>
        public string GetNewPassword()
        {
            GeneraPassword();
            return password.ToString();
        }


        /// <summary>
        /// Permite establecer el número de caracteres especiales que se quieren obtener
        /// </summary>
        /// <param name="numeroCaracteresMayuscula">Número de caracteres en mayúscula</param>
        /// <param name="numeroCaracteresNumericos">Número de caracteres numéricos</param>
        /// <param name="numeroCaracteresSimbolos">Número de caracteres de símbolos</param>
        public void SetCaracteresEspeciales(
            int numeroCaracteresMayuscula
            , int numeroCaracteresNumericos
            , int numeroCaracteresSimbolos)
        {
            // Comprobación de errores
            if (numeroCaracteresMayuscula
                    + numeroCaracteresNumericos
                    + numeroCaracteresSimbolos > LongitudPassword)
                throw new ArgumentOutOfRangeException(
                    "El número de caracteres especiales no puede superar la longitud del password");

            PorcentajeMayusculas = numeroCaracteresMayuscula * 100 / LongitudPassword;
            PorcentajeNumeros = numeroCaracteresNumericos * 100 / LongitudPassword;
            PorcentajeSimbolos = numeroCaracteresSimbolos * 100 / LongitudPassword;
        }



        /// <summary>
        /// Constructor. La contraseña tendrá 8 caracteres, incluyendo una letra mayúscula, 
        /// un número y un símbolo
        /// </summary>
        public static string GetPassword()
        {
            // Se crea un método estático para facilitar el uso
            GeneradorPassword gp = new GeneradorPassword();
            return gp.GetNewPassword();
        }


        #endregion


        #region Métodos de cálculo

        /// <summary>
        /// Método que genera el password. Primero crea una cadena de caracteres 
        /// en minúscula y va sustituyendo los caracteres especiales
        /// </summary>
        private void GeneraPassword()
        {
            // Se genera una cadena de caracteres en minúscula con la longitud del 
            // password seleccionado
            password = new StringBuilder(LongitudPassword);
            for (int i = 0; i < LongitudPassword; i++)
            {
                password.Append(GetCaracterAleatorio(TipoCaracterEnum.Minuscula));
            }

            // Se obtiene el número de caracteres especiales (Mayúsculas y caracteres) 
            int numMayusculas = (int)(LongitudPassword * (PorcentajeMayusculas / 100d));
            int numSimbolos = (int)(LongitudPassword * (PorcentajeSimbolos / 100d));
            int numNumeros = (int)(LongitudPassword * (PorcentajeNumeros / 100d));

            // Se obtienen las posiciones en las que irán los caracteres especiales
            int[] caracteresEspeciales =
                    GetPosicionesCaracteresEspeciales(numMayusculas + numSimbolos + numNumeros);
            int posicionInicial = 0;
            int posicionFinal = 0;

            // Se reemplazan las mayúsculas
            posicionFinal += numMayusculas;
            ReemplazaCaracteresEspeciales(caracteresEspeciales,
                 posicionInicial, posicionFinal, TipoCaracterEnum.Mayuscula);

            // Se reemplazan los símbolos
            posicionInicial = posicionFinal;
            posicionFinal += numSimbolos;
            ReemplazaCaracteresEspeciales(caracteresEspeciales,
                 posicionInicial, posicionFinal, TipoCaracterEnum.Simbolo);

            // Se reemplazan los Números
            posicionInicial = posicionFinal;
            posicionFinal += numNumeros;
            ReemplazaCaracteresEspeciales(caracteresEspeciales,
                 posicionInicial, posicionFinal, TipoCaracterEnum.Numero);
        }



        /// <summary>
        /// Reemplaza un caracter especial en la cadena Password
        /// </summary>
        private void ReemplazaCaracteresEspeciales(
                                        int[] posiciones
                                        , int posicionInicial
                                        , int posicionFinal
                                        , TipoCaracterEnum tipoCaracter)
        {
            for (int i = posicionInicial; i < posicionFinal; i++)
            {
                password[posiciones[i]] = GetCaracterAleatorio(tipoCaracter);
            }
        }



        /// <summary>
        /// Obtiene un array con las posiciones en las que deberán colocarse los caracteres
        /// especiales (Mayúsculas o Símbolos). Es importante que no se repitan los números
        /// de posición para poder mantener el porcentaje de dichos carácteres
        /// </summary>
        /// <param name="numeroPosiciones">Valor que representa el número de posiciones
        /// que deberán crearse sin repetir</param>
        private int[] GetPosicionesCaracteresEspeciales(int numeroPosiciones)
        {
            List<int> lista = new List<int>();
            while (lista.Count < numeroPosiciones)
            {
                int posicion = semilla.Next(0, LongitudPassword);
                if (!lista.Contains(posicion))
                {
                    lista.Add(posicion);
                }
            }
            return lista.ToArray();
        }


        /// <summary>
        /// Obtiene un carácter aleatorio en base a la "matriz" del tipo de caracteres
        /// </summary>
        private char GetCaracterAleatorio(TipoCaracterEnum tipoCaracter)
        {
            string juegoCaracteres;
            switch (tipoCaracter)
            {
                case TipoCaracterEnum.Mayuscula:
                    juegoCaracteres = caracteres.ToUpper();
                    break;
                case TipoCaracterEnum.Minuscula:
                    juegoCaracteres = caracteres.ToLower();
                    break;
                case TipoCaracterEnum.Numero:
                    juegoCaracteres = numeros;
                    break;
                default:
                    juegoCaracteres = simbolos;
                    break;
            }

            // índice máximo de la matriz char de caracteres
            int longitudJuegoCaracteres = juegoCaracteres.Length;

            // Obtención de un número aletorio para obtener la posición del carácter
            int numeroAleatorio = semilla.Next(0, longitudJuegoCaracteres);

            // Se devuelve una posición obtenida aleatoriamente
            return juegoCaracteres[numeroAleatorio];
        }

        #endregion

    }
}

 

La rutina puede probarse gracias a los amigos de velasco.biz: Probar rutina

Anuncios

Posted in C#, Helpers, NetFramework | 3 Comments »

C# Personalizar la ordenación de listas con IComparable

Posted by alskare en 19/09/2009

Hace poco, designios extraños de la vida, me ha tocado hacer una ordenación de una serie de objetos string tal como se hacía en la antigüedad. Cuando hablo de la antigüedad, tan sólo dejo correr un poco la memoria hasta los tiempos en que vestía con un jersey de cuello alto y unos pantalones cortos. Entonces, al pasar por los pasillos de las aulas, no era nada extraño escuchar una especie de canto gregoriano (pero a lo pobre) en el que una panda de alumnos que pensaban más en el pájaro del árbol de enfrente que en lo que estaban haciendo, solían recitar: a, b, c, ch (che), d, e , f, g, h, i, j, k, l, m, n, ñ, o, p, q, r, rr (erre doble), s, t, u, v, w, x, y, y zeta.

Bueno, el caso es que esta vez no me ha tocado hacer nada sobre la rr, pero sí que he tenido que hacer un sistema de ordenación en el que quedase contemplado la letra che. Por cierto, dicen que la curiosidad mata al gato y, como soy muy cotilla, se me ha ocurrido buscar la letra en la R.A.E y, ¡sorpresa!, ¡Todavía existe! (http://buscon.rae.es/draeI/SrvltConsulta?TIPO_BUS=3&LEMA=che).

Siempre me voy por las ramas, así que… al lío:

Vamos a crear un ejemplo con la socorrida clase Persona y, como haremos uso de una colección List<Persona>, le implementamos la interfaz IComparable. Aunque ya sabemos que el resultado no será el que andamos buscando, nos dará una idea de cómo nos realizaría la ordenación cualquier colección que nos permita realizar una ordenación de los objetos Persona.

using System;
using System.Collections.Generic;

namespace OrdenacionStrings
{
    class Ordenando
    {
        static void Main(string[] args)
        {
            List<Persona> gente = new List<Persona>();
            gente.Add(new Persona("Jaime", "Cebado Sánchez"));
            gente.Add(new Persona("Victoria", "Chacón Gómez"));
            gente.Add(new Persona("Anibal", "Chacón Gómez"));
            gente.Add(new Persona("Andrés", "Cullera Ramón"));
            gente.Add(new Persona("Juan Manuel", "Damián Rodriguez"));

            gente.Sort();

            foreach (Persona p in gente)
            {
                Console.WriteLine(p.Apellidos + ", " + p.Nombre);
            }
            Console.ReadKey();
        }
    }


    public class Persona : IComparable
    {
        public string Nombre { get; set; }
        public string Apellidos { get; set; }

        public Persona(string nombre, string apellidos)
        {
            Nombre = nombre;
            Apellidos = apellidos;
        }

        public int CompareTo(object obj)
        {
            try
            {
                Persona p = (Persona)obj;
                return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
            }
            catch { return 0; }
        }
    }

}

En el momento en que ejecutemos el código, veremos que la ordenación que hace es la actual, es decir, tendremos un resultado en el cual, los “Chacón” estarán antes de “Cullera”, cuando en realidad deberían salir después, puesto que contienen la letra (o dígrafo) che.

Cebado Sánchez, Jaime
Chacón Gómez, Anibal
Chacón Gómez, Victoria
Cullera Ramón, Andrés
Damián Rodriguez, Juan Manuel

Una de las ventajas de la Interfaz IComparable es que podemos implementar el método CompareTo como nos plazca (gracias, OO) y, con una serie de pequeños cambios en la clase Persona, podemos realizar un control de tal manera que, analizando las dos primeras letras de la cadena, podemos indicarle si la ch irá antes o después de la c.

Así, con una implementación distinta de la anterior del métodos CompareTo, el resultado será el deseado desde el principio:

public int CompareTo(object obj)
 {
     try
     {
         Persona p = (Persona)obj;
         if (!this.Apellidos.ToLower().StartsWith("ch"))
         {
             return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
         }
         else
         {
             if (!p.Apellidos.ToLower().StartsWith("ch"))
             {
                 return (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") * -1);

                 /* la línea anterior intenta simplificar el conjunto de operaciones siguientes.
                  * Sólo se trata de realizar una inversión en la comparación de la ch y la d
                  */ 

                 //if (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") == 1)
                 //    return -1;
                 //else if (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") == -1)
                 //    return 1;
                 //else return 0;
             }
             else
             {
                 return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
             }
         }
     }
     catch { return 0; }
 }
    }

Resultado obtenido:

Cebado Sánchez, Jaime
Cullera Ramón, Andrés
Chacón Gómez, Anibal
Chacón Gómez, Victoria
Damián Rodriguez, Juan Manuel

 

Dejo aquí el código de la clase al completo para no tener que ir buscando entre los “cachitos” de código del post.

using System;
using System.Collections.Generic;

namespace OrdenacionCorrecta
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Persona> gente = new List<Persona>();
            gente.Add(new Persona("Jaime", "Cebado Sánchez"));
            gente.Add(new Persona("Victoria", "Chacón Gómez"));
            gente.Add(new Persona("Anibal", "Chacón Gómez"));
            gente.Add(new Persona("Andrés", "Cullera Ramón"));
            gente.Add(new Persona("Juan Manuel", "Damián Rodriguez"));

            gente.Sort();

            foreach (Persona p in gente)
            {
                Console.WriteLine(p.Apellidos + ", " + p.Nombre);
            }
            Console.ReadKey();
        }
    }


    public class Persona : IComparable
    {
        public string Nombre { get; set; }
        public string Apellidos { get; set; }

        public Persona(string nombre, string apellidos)
        {
            Nombre = nombre;
            Apellidos = apellidos;
        }


        public int CompareTo(object obj)
        {
            try
            {
                Persona p = (Persona)obj;
                if (!this.Apellidos.ToLower().StartsWith("ch"))
                {
                    return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
                }
                else
                {
                    if (!p.Apellidos.ToLower().StartsWith("ch"))
                    {
                        return (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") * -1);
                    }
                    else
                    {
                        return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
                    }
                }
            }
            catch { return 0; }
        }
    }
    
}

Posted in C#, NetFramework | 2 Comments »

Escribir texto en una fotografía

Posted by alskare en 26/08/2009

En más de una ocasión he tenido la necesidad de añadir algún texto dentro de una fotografía, bien sea como marca de agua para subir las fotos a un servidor o, sencillamente, para incluir algún tipo de información en la misma fotografía.

Siempre había realizado esta acción en VisualBasic, pero en esta ocasión he tenido que hacerlo en C# así que, siguiendo la costumbre, dejo aquí el trabajo. Tal como está la clase en estos momentos, reconozco que está muy verde, puesto que, sin problemas, pueden añadirse multitud de servicios que la harían mucho más funcional, pero deja bien asentadas las bases de la escritura de un texto dentro de la foto.

 

using System.Drawing;

namespace jnSoftware.Utiles.Fotos
{

    public class Foto
    {
        #region Campos 
        private Bitmap _foto = null;
        private int _posicion_izquierda = 2;
        private int _posicion_arriba = 2;
        private Brush _pincel = Brushes.White;
        private int _tamanoFuente = 100;
        private string _nombreFuente = "Tahoma";
        #endregion 


        #region Propiedades

        /// <summary>
        /// Obtiene o establece el pincel (color del texto)
        /// </summary>
        public Brush Pincel
        {
            get { return _pincel; }
            set { _pincel = value; }
        }
    

        /// <summary>
        /// Obtiene o establece el tamaño de letra
        /// </summary>
        public int Tamano
        {
            get { return _tamanoFuente; }
            set { _tamanoFuente = value; }
        }


        /// <summary>
        /// Obtiene o establece la distancia existente entre el borde izquierdo de la 
        /// foto y el principio del texto
        /// </summary>
        public int PosicionIzquierda
        {
            get { return _posicion_izquierda; }
            set { _posicion_izquierda = value; }
        }


        /// <summary>
        /// Obtiene o establece la distancia existente entre el borde superior de la 
        /// foto y el principio del texto
        /// </summary>
        public int PosicionArriba
        {
            get { return _posicion_arriba; }
            set { _posicion_arriba = value; }
        }

        #endregion


        #region Constructores
        /// <summary>
        /// Constructor - Instancia la clase con una nueva fotografía
        /// </summary>
        /// <param name="foto">Fotografía a retocar</param>
        public Foto(Bitmap foto)
        {
            _foto = foto;
        }

        /// <summary>
        /// Constructor - Instancia la clase con un fichero 
        /// </summary>
        /// <param name="foto">Ruta completa del fichero gráfico a retocar</param>
        public Foto(string foto)
        {
            _foto = new Bitmap(foto);
        }
        #endregion 




        /// <summary>
        /// Obtiene la foto con las modificaciones 
        /// </summary>
        /// <returns></returns>
        public Bitmap GetFoto()
        {
            return _foto ;
        }



        /// <summary>
        /// Escritura de la cadena de texto sobre la foto.
        /// </summary>
        /// <param name="cadena"></param>
        public void EscribeTexto(string cadena)
        {
            // La clase Graphics encapsula una superficie de dibujo de GDI+. 
            Graphics g = Graphics.FromImage(_foto);
            // Se crea un PointF para posicionar el texto
            PointF posicion = new PointF( _posicion_izquierda, _posicion_arriba);
            // Fuente
            Font fuente = new Font(_nombreFuente, _tamanoFuente);
            // Se escribe la cadena de texto en la fotografía
            g.DrawString(cadena, fuente, _pincel, posicion);
        }


    }
}

Posted in C#, Helpers, NetFramework | Comentarios desactivados en Escribir texto en una fotografía

Enviar correo electrónico desde ASP.NET

Posted by alskare en 19/08/2009

Ya sé que es un tema repetido hasta la saciedad, pero en este caso, incluyo el código de un modo egoísta, así, cuando necesite crear de nuevo un elemento que permita enviar correo, sólo tengo que hacerme a mí mismo un Copy & Paste.

El ejemplo siguiente está pensado como una utilidad de notificación en una página Web, es decir, se conocen las direcciones de origen y destino con antelación y no es necesario el uso de adjuntos.

using System;
using System.Net;
using System.Net.Mail;


/// <summary>
/// Útiles de la aplicación
/// </summary>
public static class Utiles
{


    /// <summary>
    /// Envío de un correo electrónico
    /// </summary>
    /// <param name="subject">Asunto del mensaje</param>
    /// <param name="body"></param>
    public static void EnviaMensaje(string subject, string body)
    {
        try
        {
            string smtpServidor = "SERVIDOR_SMTP";
            string smtpUsuario = "CUENTA_USUARIO_STMP";
            string smtpPassword = "PASSWORD_SMTP";
            string cuentaDestino = "CUENTA_DESTINO";
            string cuentaOrigen = "CUENTA_ORIGEN";

            // Credenciales de autenticación
            NetworkCredential credenciales = new NetworkCredential(smtpUsuario, smtpPassword);
            // Permite a las aplicaciones enviar mensajes de correo electrónico mediante el protocolo SMTP 
            SmtpClient cliente = new SmtpClient(smtpServidor);

            // Se descartan las credenciales por defecto
            cliente.UseDefaultCredentials = false;
            // Credenciales de autenticación
            cliente.Credentials = credenciales;
            // Los mensajes de correo electrónico se envían a un servidor SMTP a través de la red
            cliente.DeliveryMethod = SmtpDeliveryMethod.Network;

            // Dirección de destino
            MailAddress direccionDestino = new MailAddress(cuentaDestino, "Formulario Web");

            // Dirección de origen
            MailAddress direccionOrigen = new MailAddress(cuentaOrigen);

            // Mensaje
            MailMessage mensaje = new MailMessage();
            mensaje.From = direccionOrigen;
            mensaje.To.Add(direccionDestino);
            mensaje.Subject = DateTime.Now.ToString() + " " + subject;
            mensaje.Body = body;
            mensaje.IsBodyHtml = false;

            // Envío del mensaje
            cliente.Send(mensaje);

            // Limpieza
            mensaje = null;
            direccionOrigen = null;
            direccionDestino = null;
            credenciales = null;
            cliente = null;
        }
        catch { }
    }


}

Posted in ASP.Net, C#, NetFramework | 1 Comment »

Eventos en VB.Net y C#

Posted by alskare en 29/07/2009

Una de las cosas que más me ha llamado la atención en el cambio de VB a C# ha sido la diferencia de cómo tratan ambos lenguajes el tema de los eventos. Me imagino que estaba demasiado acostumbrado a crear eventos en VB y, sencillamente, no contemplaba ya otras opciones. De hecho, antes del .Net, en VB 6.0, ya creábamos los eventos en aquellas “maravillosas clases”:

VB. 6.0

Ejemplo de clase de VB 6.0 con un evento definido:

' --------------------------------------------------------------------------
' PruebasContador
' Clase de pruebas de eventos
'
' alskare, Jul/09
' --------------------------------------------------------------------------
Option Explicit


' Declaración del evento público de pruebas
Public Event MiEvento(ByVal NumeroActual As Integer)


Private contador As Integer


' Rutina que realiza una cuenta y dispara el evento
Public Sub Inicio()
    contador = 0
    Dim i As Integer
    For i = 1 To 1000
        If i Mod 10 = 0 Then
            RaiseEvent MiEvento(i)
        End If
    Next i
End Sub

 

Con algo tan simple como esto (la verdad es ahora lo veo simple, pero reconozco que me costó lo suyo entenderlo en su día), era frecuente añadir la rutina que capturaba el evento en algún que otro formulario, más o menos como el ejemplo que incluyo a continuación:

' --------------------------------------------------------------------------
' Form: EjecutaPruebas
' Formulario de ejecución de las pruebas
'
' alskare, Jul/09
' --------------------------------------------------------------------------
Option Explicit


' Declaración de la clase
Private WithEvents c As PruebasContador



Private Sub Form_Load()
    ' Instancia de la clase
    Set c = New PruebasContador
    ' Ejecución del módulo de la clase que inicia el contador y dispara el
    ' evento
    c.Inicio
End Sub


' Captura del evento. En el ejemplo sólo muestra el resultado en la ventana
' de inmediato.
Private Sub c_MiEvento(ByVal NumeroActual As Integer)
    Debug.Print NumeroActual
End Sub

 

Bueno, el caso es que, acostumbrado a esto, no noté una gran diferencia en VB.Net y enseguida me acostumbré a hacer uso de las facilidades que me suministraba el .Net Framework como el usar clases como parámetros en los eventos.

VB.NET

Así, una hipotética adaptación de VB6 a VB.Net del ejemplo del contador anterior podía haber sido algo como lo siguiente:

 

' -----------------------------------------------------------------------------
' Pruebas de eventos
' 
' alskare, Jul/09
' -----------------------------------------------------------------------------
Option Explicit On
Option Strict On


Namespace jnSoftware.Pruebas.Eventos

    ''' <summary>
    ''' ContadorEvents. Clase que se usará como parámetro en el disparador.
    ''' La clase contiene la propiedad Contador que incluirá el valor a "traspasar" entre
    ''' la clase disparadora y la clase receptora
    ''' </summary>
    Public Class ContadorEvents
        Inherits EventArgs

        Private _contador As Integer
        Public Property Contador() As Integer
            Get
                Return _contador
            End Get
            Set(ByVal value As Integer)
                _contador = value
            End Set
        End Property
    End Class


    ''' <summary>
    ''' PruebasContador. Al igual que en el ejemplo de VB6, sólo contiene un método que 
    ''' dispara el evento
    ''' </summary>
    Public Class PruebasContador

        ' Declaración del evento público de pruebas
        Public Event MiEvento(ByVal sender As Object, ByVal e As ContadorEvents)

        Private _contador As Integer = 0
        Private _campoContadorEvents As ContadorEvents = New ContadorEvents()

        Public Sub Inicio()
            For i As Integer = 1 To 1000
                If i Mod 10 = 0 Then
                    _campoContadorEvents.Contador = i
                    RaiseEvent MiEvento(Me, _campoContadorEvents)
                End If
            Next
        End Sub
    End Class


    ''' <summary>
    ''' Clase que ejecuta las pruebas. 
    ''' </summary>
    ''' <remarks></remarks>
    Public Class PruebasEventosVB

        Private Shared WithEvents c As New PruebasContador


        Public Shared Sub Main()
            c.Inicio()
            Console.ReadKey()
        End Sub


        ' Cada vez que se dispara el evento en la clase PruebasContador, se ejecuta este método pudiendo leer el 
        ' valor del contador gracias al parámetro del contador e.Contador
        Private Shared Sub MuestraProgreso(ByVal sender As Object, ByVal e As ContadorEvents) Handles c.MiEvento
            Console.WriteLine("Número {0}", e.Contador)
        End Sub

    End Class

End Namespace

 

He intentado seguir la misma nomenclatura que la usada en el ejemplo de VB6 para poder realizar las comparaciones. Es de destacar el método MuestraProgreso, que queda “vinculado” al disparador gracias a la cláusula Handles que se incluye después de la firma estándar. La cláusula Handles es de lo que me llamó más la atención puesto que podía vincularse un mismo método a los eventos de varios objetos, siempre que incluyesen la misma firma (ideal para capturar eventos de controles de formularios).

La vinculación del evento al método podríamos haberla creado de modo dinámico sin incluir la cláusula Handles. Esto me ha sido muy práctico cuando se trata de crear controles dinámicamente o cuando he tenido algún que otro problema de rendimiento al dispararse demasiadas veces algún evento. Así, una modificación que podríamos realizar en la clase PruebasEventos para que se vincule el evento de un modo dinámico sería algo así como:

 

Public Class PruebasEventosVB

    Private Shared WithEvents c As New PruebasContador


    Public Shared Sub Main()

        ' Vinculación del evento al módulo MuestraProgreso
        AddHandler c.MiEvento, AddressOf MuestraProgreso

        c.Inicio()

        ' Casi del mismo modo se podría desvincular
        RemoveHandler c.MiEvento, AddressOf MuestraProgreso

        Console.ReadKey()
    End Sub


    Private Shared Sub MuestraProgreso(ByVal sender As Object, ByVal e As ContadorEvents)
        Console.WriteLine("Número {0}", e.Contador)
    End Sub

End Class

 

Como podemos apreciar, entre VB 6 y VB.Net no existen grandes diferencias. Tan sólo es cuestión de ir habituándose a las nuevas características que nos ofreció .Net. No obstante, el día que me empeciné en empezar a crear las aplicaciones en C#, la cosa no ha sido tan fácil.

C#

Para empezar, resulta que no existe la cláusula Handles de VB ni, que yo sepa, nada que se le parezca, así que habrá que hacer uso de una vinculación dinámica al igual que se ha creado en el último listado. De hecho, en este último listado, al hacer uso de AddHandler, sin saberlo ya hemos estado empleando algo nuevo para aquellos que nos hemos movido siempre en VB: los delegados. Aunque cueste un poco el entendimiento de este concepto, yo he creído entender que un delegado es una función anónima que, sencillamente, tiene la dirección de memoria de la función.  Pues eso, que ya en VB hemos usado los delegados pero, como dice el maestro Guille, VB es muy protector con nosotros y nos protege de ciertas cosas que no necesitamos saber, o que no es obligatorio que sepamos.

El caso es que para poder crear una vinculación dinámica necesitamos que sea una función la que se encargue de “recepcionar” los datos que queremos traspasar al disparar el evento, así que, además del evento, deberemos crear un delegado para que pueda crear tal vinculación (jo, qué rollo, ¿no?). Mejor lo vemos con un ejemplo:

 

using System;


namespace jnSoftware.Pruebas.Eventos
{


    /// <summary>
    /// ContadorEvents. Clase que se usará como parámetro en el disparador
    /// </summary>
    public class ContadorEvents: EventArgs 
    {
        public int Contador { get; set; }
    }



    public class PruebasContador
    {

        // Definición del delegado
        public delegate void MiEventoDelegate(object sender, ContadorEvents e);

        // Definición del evento
        public event MiEventoDelegate MiEvento;


        private int contador = 0;
        private ContadorEvents campoContador = new ContadorEvents();

        public void Inicio()
        {
            for (int i = 0; i <= 1000; i++)
            {
                if (i % 10 == 0)
                {
                    campoContador.Contador = i;
                    if (MiEvento != null)
                        MiEvento(this, campoContador);
                }
            }
        }
    }




    /// <summary>
    /// Clase que ejecuta las pruebas
    /// </summary>
    public class PruebasEventosC
    {

        private static PruebasContador c = new PruebasContador();

        static void Main()
        {
            // Vinculación entre el evento y la función MuestraProgreso. Nos fijamos que 
            // se hace uso del delegado definido en la clase PruebasContador
            c.MiEvento += new PruebasContador.MiEventoDelegate(MuestraProgreso);
            
            c.Inicio();

            // Al igual que se ha hecho con anterioridad, se desvincula el evento
            c.MiEvento -= new PruebasContador.MiEventoDelegate(MuestraProgreso);
        
            Console.ReadKey();
        }

     
        static void MuestraProgreso(object sender, ContadorEvents e) 
        {
            Console.WriteLine("Número {0}", e.Contador);
        }
    }

}

 

Como se puede apreciar, las máximas diferencias podemos encontrarlas en la clase PruebasContador, sobre todo porque, frente a VB, define el Delegado antes mencionado y, antes de lanzar el evento, realiza una comprobación para ver si existen suscripciones a MiEvento.

Bueno, ahí queda. Por supuesto, creando esta entrada no he intentado escribir nada nuevo del tema, puesto que existe, ya en internet miles de artículos que destripan mucho mejor que yo todo lo referente a los eventos, pero sí creo haber puesto mi granito de arena para todos aquellos que, como me ha pasado a mí, se han encontrado con la aparición de los delegados a la hora de pasar a programar en C#.

Posted in C#, NetFramework, Vb.Net | Comentarios desactivados en Eventos en VB.Net y C#

Buscar cadenas con Regex ( C#).

Posted by alskare en 27/07/2009

No será la primera vez que me encuentro con algún tipo de rutina en la que tengo que encontrar si existe o no alguna palabra (o símbolo) dentro de una cadena. Algo así como : ¿Se encuentra la cadena “la” dentro del familiar “Hola Mundo”?. Si el lector es tan torpe como yo, seguro que habrá creado alguna que otra rutina que, aunque seguro que ha servido para salir del paso, nos ha dejado algún que otro mal sabor de boca por el rendimiento, sobre todo, cuando han cambiado los patrones de búsqueda.

Una simple combinación de instrucciones en las que interviene un poco de Expresiones Regulares nos ayuda enormemente en la tarea y nos permite averiguar, en el ejemplo siguiente si las cadenas de PatronBusqueda se encuentran en las diferentes frases que se muestran por la Console.

 

// Ejecución de las pruebas.
public static void Main()
{
    // Patrón de búsqueda, palabras que queremos saber si existen
    string PatronBusqueda = "la|UNO|DOS|TRES|CUATRO|CINCO|SEIS|SIETE|OCHO|NUEVE|DIEZ";

    // Instancia de la clase Regex. Se indica el patrón de búsqueda con las opciones 
    Regex regex = new Regex(PatronBusqueda,
       RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);

    // Las siguientes líneas muestran la frase y si se han encontrado o no las cadenas del patrón
    Console.WriteLine("{0} ---> {1}", "Hola Mundo", regex.Match("Hola Mundo").Success);
    Console.WriteLine("{0} ---> {1}", "Prueba número uno", regex.Match("Prueba número uno").Success);
    Console.WriteLine("{0} ---> {1}", "Prueba número CINCO", regex.Match("Prueba número CINCO").Success);
    Console.WriteLine("{0} ---> {1}", "Prueba número SiEtE", regex.Match("Prueba número SiEtE").Success);
    Console.WriteLine("{0} ---> {1}", "Va a ser que no", regex.Match("Va a ser que no").Success);


    Console.ReadKey();
}

 

Supongo que a cualquier persona habituada a los Regex, este pequeño ejemplo le parecerá una auténtica tontería, pero os aseguro que, para mí, el descubrimiento de los Regex (por lo menos para estas cosas sencillitas, que no me atrevo con más) ha sido “uno de los grandes inventos del siglo”.

Posted in C#, NetFramework | Comentarios desactivados en Buscar cadenas con Regex ( C#).