Tag Archive for .net

String – IsNullOrWhiteSpace

Algo sumamente común en el chequeo de contracts y en la verificación de inputs o entradas es revisar si la entrada es nula o esta vacía. Algo que me topé hace unos días fue ver esta muestra de alguien que preguntaba algo

' Aquí va más código
If cmbEmpresa.Text = "" Then
    MessageBox.Show("Seleccione una empresa")
End If
' Aquí continua más código

La primera línea asume varias opciones que pueden no ser ciertas, ¿y si la cadena es nula?, ¿qué tal si la cadena contiene solamente espacios en blanco?. Probablemente para validar tales casos terminaremos con algo como esto

If String.IsNullOrEmpty(cmbEmpresa.Text) _
  OrAlso cmbEmpresa.Text.Trim().Length = 0 Then
    MessageBox.Show("Seleccione una empresa")
End If

Algo "verbose" a mi parecer, podríamos escribir un método de extensión que nos ayude y simplifique tal comparación (se los dejo de tarea). En la Framework 4 ahora existe un método nuevo, IsNullOrWhiteSpace, que nos simplifica la tarea

If String.IsNullOrWhiteSpace(cmbEmpresa.Text) Then
    MessageBox.Show("Seleccione una empresa")
End If

Enumeradores – Bit Flags

Todos recordamos que podemos usar los enumeradores como flags y luego recuperar la selección con operaciones de bit, por ejemplo, tomemos el siguiente enumerador (tomado de la documentación de MSDN):

[Flags]
public enum MultiHue {
    Black = 0,
    Red = 1,
    Green = 2,
    Blue = 4
}

Podemos pedirle al usuario de la API que seleccione un set de colores o tonalidades y luego obtener su selección mediante bit operations:

var hue = MultiHue.Black | MultiHue.Red;
var isRed = ((hue & MultiHue.Red) == MultiHue.Red);
var isGreen = ((hue & MultiHue.Green) == MultiHue.Green);

Bien, ahora en la .Net Framework 4.0 tenemos un nuevo método que nos hace mas “claro” la lectura de la bit flag:

var hue = MultiHue.Black | MultiHue.Red;
var isRed = hue.HasFlag(MultiHue.Red);
var isGreen = hue.HasFlag(MultiHue.Green);

No se ustedes pero este último es de mejor lectura para mi.

¡Saludos!

Workflow Foundation, Cuándo, Cómo, Dónde

Una pregunta común que recibo a diario es "cúando debo usar Workflow Foundation, en mi lógica de negocio? en mi cliente? como un almacen de persitencia?". Hace unos días un amigo me escribió un correo comentandome esto:

“Entonces en Workflow Foundation debo reemplazar mi lógica de negocio hecha en código por un workflow secuencial”

Esta pregunta coincide con algo sumamente recurrente que suelo escuchar cada vez que hablo acerca de Workflow Foundation: Dónde encaja Workflow Foundation en mi aplicación

Recuerdo a finales del año 2005 cuando las primeras Beta de la Framework 3.0 o “previews” salieron al aire, recuerdo haberme emocionado muchísimo al leer sobre las “Foundations” (de hecho, en ese tiempo estaba cambiando de plataforma y vivía en un período gris y no podía evitar el comparar cosas en .Net con cosas en lo “otro” en que trabajaba). Una de las cosas que más me llamó la atención fue Workflow Foundation, probablemente porque en uno de nuestros proyectos anteriores pasé un buen tiempo desarrollando una máquina secuencial de flujo y bueno me pareció excelente el que ahora una framework incluyera todo un toolset y “runtime” para el manejo de flujos de trabajo.

Hagamos antes un repaso rápido de qué involucra Workflow Foundation (para conocer un poco más acerca de que hace o se trata, recomiendo este link): http://msdn.microsoft.com/en-us/netframework/aa663328.aspx

  • Un runtime para ejecución de procesos (workflows)
  • Una serie de bloques o actividades básicas para la construcción de procesos (Activities)
  • Un toolset completo para el manejo y diseño de procesos (Addins, Services)

Adicionalmente la framework completa puede extenderse, o sea, embeber el runtime en nuestra aplicación, extender los flujos actuales, crear nuestras propias actividades y servicios y hasta embeber el designer de Workflows en nuestra aplicación.

Bien, entonces nace la pregunta de oro, ¿cuándo usar Workflow Foundation?. Bien, podríamos decir que WF puede usarse en las siguientes situaciones

  • Orquestación de pasos o actividades dentro de un modelo de negocio
  • Visibilidad del proceso del negocio
  • Persistencia en un proceso de negocio

Y bueno, para evitar confusiones, vale la pena mencionar cuándo a mi criterio no debe utilizarse Workflow Foundation

  • Para desarrollar la lógica de negocio del sistema
  • Para desarrollar una herramienta de programación para personal no técnico
  • Para reemplazar un DSL ya establecido en la organización
  • Para reemplazar la persistencia de otros aspectos que no están relacionados al proceso en si

Esto es importante, especialmente la primera parte, he visto muchos sistemas que buscan reemplazar o diseñar la lógica de negocio del sistema completamente desde cero usando WF, creo que ese es el ingrediente principal para futuros dolores de cabeza.

En vez de reemplazar o crear la lógica de negocio completamente desde WF, porqué mejor no “orquestamos” esa lógica mediante WF? Un buen indicador de que estamos empleando mal WF es la proliferación de codebehind en el Workflow y de Code Activities. Un buen Workflow debe ser netamente declarativo, eso elimina el uso de Code Activities, de hecho, en Workflow Foundation 4.0 las Code Activities ya no existen.

En resumidas cuentas, Workflow Foundation deben ser un agregado a la lógica de nuestra entidad de negocio y no el core de ella. Debe funcionar como un coordinador entre los pasos o secuencias de cada uno de los servicios o actividades de un proceso actual, debe ser una herramienta de extensión, no una central del desarrollo.

Bueno, como siempre, cuéntenme sus ideas, perspectivas o comentarios al respecto. Soy todo oídos :)

The complex way is not really the right way…

Today I was visiting a client, and I just stop and watch something that really caught my attention:

foreach (item in myList) {
    if (item.Length > 0) {
        SaveItem(item);
    }
}
// more ugly code...

Well, the code was really different (an uglier one) but you got the idea…. What I suggested to my client was a more concise solutio, create an extension method:

public static class IEnumerableExt {
    public static IEnumerable<T> ForEach(this IEnumerable<T> items, Action<T> action) {
        foreach(var item in items) {
            action(e);
        }
    }
}

The second part is the easy one…

myList
    .Where((item) => item.Length > 0)
    .ForEach((item) => SaveItem(Item));

I don’t know you, but this way looks a lot clearer to me…

Lessson learned: If your code looks complex, please, take a time and look how to make it more clearer.

Stream Transformations

Recuerdo que una de las primeras cosas que solíamos aprender con un lenguaje o framework era el cómo leer/escribir de o hacia un archivo (o por lo menos antes así era). En la .Net Framework tenemos todo un namespace dedicado a las operaciones de E/S de datos e información (System.IO). Una de las clases básicas en este namespace es la clase abstracta Stream. Cómo en la mayoría de lenguajes y frameworks modernas,Stream encapsula o abstrae las operaciones de E/S hacia diversos orígenes y/o destinos, de esa manera tenemos cosas como Network Streams, File Streams, Memory Streams, etc…

Algo de lo cual no muchos nos habíamos percatado es que las clases derivadas de Stream no solamente abstraen operaciones de E/S, sino que también pueden ser usadas para “transformar” datos sobre la marcha, de esa manera tenemos ya en la .Net framework clases Stream especiales como GzipStream(Compresión/Descompresión), CryptoStream (Encriptación) y me imagino que hay muchas más en la framework. Hace ya varios meses leí en la MSDN Magazine (una de mis magazines favoritas por cierto, les recomiendo a todos suscribirse :P ) un excelente artículo sobre Testing Transformation Streams (TS de aquí en adelante) y en general de que se trataba una TS y como crearla/utilizarla. Un ejemplo típico de una transformation Stream es una que elimine los espacios en una Stream básica, por ejemplo:

using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace FilterSpacesStream
{
    public class NoSpaceStream : Stream
    {
        public NoSpaceStream(Stream sink)
        {
            Sink = sink;
        }

        private Stream Sink { get; set; }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return Sink.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get { return Sink.Length; }
        }

        public override long Position
        {
            get { return Sink.Position; }
            set { Sink.Position = value; }
        }

        public override void Flush()
        {
            Sink.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return Sink.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            Sink.SetLength(value);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return Sink.Read(buffer, offset, count);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            string text = Encoding.UTF8.GetString(buffer);
            string result = Regex.Replace(text, @"s", string.Empty);

            byte[] data = Encoding.UTF8.GetBytes(result);
            Sink.Write(data, 0, data.Length);
        }
    }
}

Y su respectivo Fact Fixture:

using System.IO;
using System.Text;
using Xunit;

namespace FilterSpacesStream
{
    public class NoSpaceStreamFacts
    {
        private readonly MemoryStream output = new MemoryStream();
        private readonly NoSpaceStream sut;

        public NoSpaceStreamFacts()
        {
            sut = new NoSpaceStream(output);
        }

        [Fact]
        public void It_can_read()
        {
            Assert.Equal(output.CanRead, sut.CanRead);
        }

        [Fact]
        public void It_can_write()
        {
            Assert.True(sut.CanWrite);
        }

        [Fact]
        public void It_can_Seek()
        {
            Assert.Equal(output.CanSeek, sut.CanSeek);
        }

        // And many other operations I really don't want to test...

        [Fact]
        public void When_write_a_string_with_spaces_it_returns_a_string_without_spaces()
        {
            string text = "This is a test ";
            byte[] buffer = Encoding.UTF8.GetBytes(text);
            sut.Write(buffer, 0, buffer.Length);
            sut.Flush();

            buffer = output.ToArray();
            string converted = Encoding.UTF8.GetString(buffer);
            Assert.Equal("Thisisatest", converted);
        }
    }
}

Uno de los usos básicos que podríamos darle a las Transformation Streams es crear una que sobre la marcha me comprima/cambie/elimine datos en la Render Stream utilizada por la ASP.NET Framework, ven ahora hacia dónde estoy orientando este post? (Rayos, creo que pensé en “voz” alta :P )

Bueno, hasta la siguiente entrega!