martes, 24 de noviembre de 2015

Crear índices en EF con Fluent API

Crear un índice vía Fluent API ha resultado ser una tarea nada intuitiva y más difícil de lo esperado. Es por ello que lo voy a dejar por escrito y así, siguientes veces, no tendré que navegar por mi código para buscar la chuleta de cómo hacerlo, porque en mi opinión es un código irrecordable y que no nunca logro descubrir a golpe de Intellisense.

Partiendo de una entidad muy sencilla como la siguiente:

class Customer

{

    public int Id { get; set; }

    public string Name { get; set; }

    public string Country { get; set; }

    public DateTime CreatedDate { get; set; }

}

Hacer un índice único por el campo Name sería asi:

modelBuilder

    .Entity<Customer>()

    .Property(t => t.Name)

    .HasMaxLength(250)

    .HasColumnAnnotation(IndexAnnotation.AnnotationName,

        new IndexAnnotation(new IndexAttribute() { IsUnique = true }));

Si esto ya parece un código algo enrevesado, crear un índice por varios campos es aun mas sorpredente porque es imprescindible dar un nombre al índice y una posición a cada campo:

var customer = modelBuilder.Entity<Customer>();

const string indexName = "IX_Customers";

customer

    .Property(t => t.Name)

    .HasMaxLength(250)

    .HasColumnAnnotation(

        IndexAnnotation.AnnotationName,

        new IndexAnnotation(

            new IndexAttribute(indexName, 1)

            {

                IsUnique = true

            }));

customer

    .Property(t => t.Country)

    .HasMaxLength(250)

    .HasColumnAnnotation(

        IndexAnnotation.AnnotationName,

        new IndexAnnotation(

            new IndexAttribute(indexName, 2)

            {

                IsUnique = true

            }));

El poner un tamaño a las propiedades de tipo string es porque si no se mapearán a nvarchar(max) y la creación del índice fallará en Sql Server.

Originalmente el post llevaba hasta aquí, pero el bueno de @gulnor me incito me incitó de mala manera a dar una solución y no sólo exponer un problema, y claro, después de leer su reciente y reflexivo post Código abierto, ecosistema cerrado, me he sentido un poco culpable y no he tenido otra que corresponder con una posible solución para que que así no se diga que no contribuyo (o al menos lo intento) :)

La idea es poder crear un índice sin todo el ruido que genera el código anterior, algo asi:

modelBuilder.Entity<Customer>().Index(p => p.Name, true, "IX_Customers", 1);

modelBuilder.Entity<Customer>().Index(p => p.Country, true, "IX_Customers", 2);

modelBuilder.Entity<Customer>().Index(p => p.CreatedDate);

El código con los métodos extensores es el siguiente (en el que no me gusta el nombre del parámetro genérico T2 y además reconozco abusar de la indentación, pero es por un buen motivo, para facilitar la lectura en el blog):

static class ModelBuilderExtensions

{

    private static void CreateIndex(

        PrimitivePropertyConfiguration property,

        bool isUnique = false,

        string name = null,

        int position = 1)

    {

        var indexAttribute = new IndexAttribute();

        if (!string.IsNullOrWhiteSpace(name))

        {

            indexAttribute = new IndexAttribute(

                name,

                position <= 0 ? 1 : position);

        }

        indexAttribute.IsUnique = isUnique;

        property.HasColumnAnnotation(

            IndexAnnotation.AnnotationName,

            new IndexAnnotation(indexAttribute));

    }

 

    public static void Index<T, T2>(

        this EntityTypeConfiguration<T> entity,

        Expression<Func<T, T2>> propertyExpression,

        bool isUnique = false,

        string name = null,

        int position = 1) where T : class where T2 : struct

    {

        var property = entity.Property(propertyExpression);

        CreateIndex(property, isUnique, name, position);

    }

 

    public static void Index<T, T2>(

        this EntityTypeConfiguration<T> entity,

        Expression<Func<T, T2?>> propertyExpression,

        bool isUnique = false,

        string name = null,

        int position = 1) where T : class where T2 : struct

    {

        var property = entity.Property(propertyExpression);

        CreateIndex(property, isUnique, name, position);

    }

 

    public static void Index<T>(

        this EntityTypeConfiguration<T> entity,

        Expression<Func<T, string>> propertyExpression,

        bool isUnique = false,

        string name = null,

        int position = 1) where T : class

    {

        var property = entity.Property(propertyExpression);

        CreateIndex(property, isUnique, name, position);

    }

}

Ahora sí, post cerrado!

Un saludo!

viernes, 6 de noviembre de 2015

Code First Migrations: Borrón y cuenta nueva

Cuando trabajas con Code First es muy habitual (por no decir casi obligatorio si no buscas alguna alternativa) el trabajar con migraciones basadas en código.

Con el paso del tiempo, te pasará que la carpeta Migrations tendrá una cantidad ingente de código que refleja el cómo ha evolucionado tu modelo. Si te resulta necesario mantener este código para poder hacer un hipotético downgrade de tu aplicación, no sigas leyendo, este post no es para ti. Sin embargo, si tienes la certeza de que tu última versión publicada es válida y no vas a querer nunca revertir los cambios, puede ser una buena idea el desprenderte de esa base de código que está ahi, pero no te va a servir para nada… bueno sí, para que VS vaya más lento y ReSharper tenga más trabajo cuando actives Solution-Wide Analysis.

Decididos ya a soltrar lastre ¿Cómo elimino todas las migraciones de __MigrationHistory y hago coincidir mi modelo con el esquema actual de la base de datos?

El proceso a seguir es el siguiente:

  • Borrar la tabla __MigrationHistory
  • Borrar la carpeta Migrations de VS
  • Ejecutar el comando Enable-Migrations
  • Ejecutar el comando Add-Migration InitialCreate
    • Que el nombre de la migración sea InitialCreate es sólo una convención, podría ser cualquier otro.
  • Comentar el código del método Up de la migracion
  • Ejecutar el comando Update-Database
    • Creará la tabla __MigrationHistory
  • Descomentar el código del método Up de la migración
    • Así al llamar a Update-Database siguientes veces y si la base de datos no existe, se creará una nueva con todos los objetos que se capturaron en este primer snapshot.

Después de esto tendremos una nueva tabla __MigrationHistory con una sola migración que coincidira con nuestro modelo.

Lógicamente, para llevar a cabo esta operación primero deberíamos tener en cuenta si hemos incluido código personalizado en el método Up de alguna migración. En caso afirmativo, deberemos analizar ese código de forma particular y ver si sigue siendo necesario en nuestro nuevo e impoluto escenario.

Un saludo!

jueves, 24 de septiembre de 2015

Visualizar el estado de las entidades en Entity Framework

Si programas con Entity Framework, tarde o temprano tendrás algún problema al querer saber cuál es el estado de todas las entidades que está siguiendo el contexto.

Claro está que puedes utilizar el método Entry o incluso saber si una propiedad está modificado con un código similar al siguiente:

DbEntityEntry<Customer> entry = context.Entry(customer);

Debug.WriteLine(entry.State);

Debug.WriteLine(entry.Property(p => p.Name).IsModified);

En cualquier caso, ver el estado de todas las entidades y que propiedades han sido modificadas (si es que la entidad ha sido modificada) se puede convertir en una tarea ardua y no trivial.

Para estos casos (yo directamente es un código sin el que no puedo vivir) encontré un día un método extensor de ObjectStateManager (DbContext lo sigue utilizando bajo cuerda) que vuelca toda la información de seguimiento en HTML, bravo!!

Con este nuevo y awesómico método, no hay sesion de debugging que se resista… porque sí, yo depuro y mucho, lo reconozco :)

image

image

He subido un proyecto muy sencillo en github donde se puede ver en un periquete como funciona el asunto.

Un saludo!

miércoles, 9 de septiembre de 2015

Transformaciones de ficheros .config con SlowCheetah

Las transformaciones de ficheros Web.config es algo habitual en proyectos Web. Una transformación consiste en aplicar al fichero Web.config una serie de transformaciones en función de la configuración de compilación seleccionada al publicar.

De serie, VS sólo permite aplicar transformaciones a los ficheros Web.config y además la previsualización de los cambios es algo enrevesada (en mi opinión).

En el menú contextual de cualquier fichero Web.config:

image

Esto agregará tanta ficheros Web.config dependientes como configuraciones de compilación tenga el proyecto:

image

Después de haber publicado el proyecto (y esto conlleva crear un perfil de publicación), tendremos disponible las siguientes opciones en el menú contextual de cualquier fichero .config (no sólo del Web.config).

image

  • Publish Web.config
    • Publica la transformación según el perfil de publicación seleccionado.
  • Preview changes to Web.config
    • Compara lo publicado (Current versión on server) con la transformación (After Publish) según el perfil de publicación seleccionado
  • Replace Web.config from server
    • Reemplaza el fichero Web.config con la versión del servidor

En lo relativo a transformaciones nos interesan las dos primeras opciones, pero “Publish Web.config” implicar tener que publicar primero (aunque sea a disco) y “Preview changes to Web.config” sólo funciona con ficheros Web.config (aunque aparezca las opción para el resto de ficheros .config).

Además, “según el perfil de publicación seleccionado” hace mención al perfil de publicación que esté activo en la ventana “Azure App Service Activity”

image

En líneas generales, no me gusta del todo como soluciona esta problemática VS y lo veo algo lioso (al menos yo lo veo así). La buena noticia es que hay soluciones como es el caso de SlowCheetah.

SlowCheetah aporta lo siguiente:

  • Permite aplicar transformaciones a cualquier fichero .config (no sólo a Web.config).
  • Permite previsualizar las transformaciones de una forma cómoda y sencilla.

Se presenta en dos distintos formatos:

Como extensión de VS nos permitirá añadir a cualquier fichero .config ficheros de transformación dependientes (excepto para Web.config, donde parece no querer pisar a la opción nativa de VS).

image

Lo que hará “Add Transform” es agregar tantos ficheros .config como configuraciones de compilación tengamos y además incluirá también un fichero .config por cada perfil de publicación (lo cierto es que no entiendo el propósito de agregar ficheros para perfiles de publicación, pero su motivo tendrá…).

La primera vez que utilicemos esta opción en el proyecto nos preguntará lo siguiente:

image 

Y en caso de aceptar se llevarán a cabo los siguientes cambios:

  • Instalará el paquete de Nuget SlowCheeath
  • Se agregará el fichero SlowCheetah.Transforms.targets en la ruta del proyecto Properties\SlowCheetah
  • Y modificará el fichero .csproj para hacer uso del anterior fichero de MSBuild

image

Lo bueno de que haya agregado el paquete de Nuget es que si compartimos el proyecto con alguien, ya no será necesario que instale la extensión en su equipo para llevar a cabo las transformaciones.

En cualquier caso, ahora tenemos disponible en cualquier fichero de transformación .config una nueva opción en el menú contextual para previsualizar la transformación de una forma sencilla y clara.

image

Un saludo!

martes, 9 de junio de 2015

Expresiones regulares en .NET

De todos es sabido que cuando tienes un problema e intentas solucionarlo con una expresión regular, acabas teniendo 2 problemas (el original y el añadido de tener que entender y mantener la expresión regular en tu código).

En cualquier caso, hay veces donde una expresión regular sí es la solución y es entonces cuando resulta necesario tener claros algunos conceptos. Inicialmente, mi escaso conocimiento sobre las expresiones regulares no era un tema que me preocupara (mas me vale por a día de hoy tampoco soy ningún experto), sabía lo que sabía y con ello sobrevivía, pero a medida que las he ido utilizando con más asiduidad (esto es en vez de “nunca” a “de pascuas a ramos”) he ido perfilando una guía de supervivencia que espero me permita enfrentarme a ellas con una mayor y “aparente” seguridad.

Para trabajar con expresiones regulares en .NET, utilizaremos la clase Regex del espacio de nombres System.Text.RegularExpressions.

Regex expone tanto métodos estáticos como métodos de instancia.

Los métodos estáticos crearán internamente una instancia de la propia clase Regex, pero cachearán la instancia creada para no tener que instanciarla de nuevo cuando se vuelva a llamar a un método estático con la misma expresión regular (el número de entradas de la caché es configurable según la propiedad CacheSize). De este modo, si sabemos de antemano que instanciaremos frecuentemente la misma expresión regular, utilizar los métodos estáticos nos reportará un mejor rendimiento.

Si trabajamos con métodos de instancia y relacionado con el rendimiento, otra opción sería compilar la expresión regular, que tardaría un poco más en instanciar el objeto pero a cambio sucesivas llamadas a la misma instancia no tendrían que interpretarla de nuevo cada vez. También podemos compilar expresiones regulares en un ensamblado con CompileToAssembly, pero lo cierto es que a día de hoy veo lejana esta opción :)

Para crear una instancia de Regex tendremos que suministrarle un patrón con la expresión regular y, opcionalmente, un opciones en un valor del tipo RegexOptions. Las opciones son modificadores para la expresión regular especificada en el patrón. Por ejemplo IgnoreCase (para no distinguir mayúsculas de minúsculas), Multiline (para cambiar el significado de ^ y $), etc. RegexOptions es un enumerado decorado con el atributo [Flags], luego acepta varias valores.

var pattern = "^sergio$";
// con RegexOptions.IgnoreCase | RegexOptions.Multiline se encontrarán 2 coincidencias, sin estas opciones no se encontrará ninguna
var regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
var input = "sergio\nSERGIO";
var match = regex.Match(input);
while (match.Success)
{
Console.WriteLine("Value {0} Index {1} Length {2}", match.Value, match.Index, match.Length);
match = match.NextMatch();
}
Si especificamos las opciones a través de RegexOptions se aplicarán a toda la expresión regular. Sin embargo, también es posible establecer estas opciones en línea en el texto de la propia expresión regular, que además permitirá entonces activar o desactivar la opción selectivamente. Por ejemplo, la anterior expresión regular podría rescribirse como sigue:
var pattern = "(?im)^sergio$";

Y aplicar las opciones de forma selectiva podemos verlo en el siguiente pantallazo que además muestra como ReSharper nos puede ayudar a depurar fácilmente nuestras expresiones regulares. Por ejemplo en esta expresión regular estamos diciendo que inicialmente sea case-insensitive y multiline pero que “gio” sea case-sensitive.

image

Otro gran problema de las expresiones regulares es que cuando vuelvas a ellas tiempo después ya no sabrás que querías buscar. En mi caso utilizo normalmente una herramienta como http://www.regexr.com/ para crear la expresión regular y, después de mucho método ensayo-error, doy con la expresión adecuada que finalmente copio y pego en mi código con algún que otro comentario más o menos afortunado…

// multiline
// al comienzo de cada línea y entre paréntesis un prefijo
// (+número) pudiendo haber espacios antes y después del/los número(s)
// opcionalmente espacios
// 9 números sin espacios para terminar la línea
var pattern = @"(?m)^\(\s*\+\s*(\d+)\s*\)\s+(\d{9})$";

Desde luego más vale ese comentario que nada, pero hay otra formas mejor de hacerlo que es incluir comentarios en la propia expresión regular. El primer tipo de comentario que podemos incluir es un comentario en línea con la sintaxis (?# comentario…)

var pattern = @"(?m)(?# multiline)^\(\s*\+\s*(\d+)\s*\)(?# prefijo)\s+(\d{9})(?# número)$";

Probablemente los comentarios en líneas sea útiles para expresiones regulares sencillas pero en el momento que sean complicadas o simplemente tengan un tamaño considerable, parece mejor usar los comentarios “hasta final de línea”. Para incluir este segundo tipo de comentarios tenemos que activar la opción/modificador IgnoreWhiteSpace que lo hace es que se ignoren los espacios en blanco en la expresión regular, habilitando de este modo que podamos incluir este nuevo tipo de comentarios con la sintaxis # comentario

var pattern = @"(?mx) # MultiLine, IgnoreWhiteSpace
^\(\s*\+\s*(\d+)\s*\) # Prefijo con paréntesis inicial y final obligatorio
# con espacios opcionales después del paréntesis inicial y antes del parántesis final
# en medio desde 1 a n dígitos contiguos
\s+ # espacios desde 1 a n
(\d{9})(?# número)$ # Teléfono de 9 dígitos contiguos
";

Otro concepto que me parece importante entender para poder avanzar y no usar sólo el método IsMatch (que está bien, evita overhead porque no captura nada, pero por contra nos estamos perdiendo todo el potencial de las expresiones regulares) es qué son las coincidencias, grupos y capturas.

Cuando buscamos coincidencias en un texto según una expresión regular, no sólo podemos comprobar que el texto coincide con nuestro patrón, sino también capturar ciertas porciones de la cadena para poder trabajar con ellas después. Esto es un grupo y es representado por el objeto Match. Para capturar un grupo utilizamos los paréntesis.

var pattern = @"(.+)@(.+)";
var regex = new Regex(pattern);
var input = "sergio @panicoenlaxbox\ncarmen @panicoenel20";
var match = regex.Match(input);
if (match.Success)
{
do
{
Console.WriteLine("Match - Value {0} Index {1} Length {2}", match.Value, match.Index, match.Length);
for (int i = 1; i < match.Groups.Count; i++)
{
Group group = match.Groups[i];
Console.WriteLine("Group - Value {0} Index {1} Length {2}", group.Value, group.Index, group.Length);
}
match = match.NextMatch();
} while (match.Success);
}

Imprime por consola lo siguiente:

Match - Value sergio @panicoenlaxbox Index 0 Length 22
Group - Value sergio Index 0 Length 6
Group - Value panicoenlaxbox Index 8 Length 14
Match - Value carmen @panicoenel20 Index 23 Length 20
Group - Value carmen Index 23 Length 6
Group - Value panicoenel20 Index 31 Length 12

Como vemos no hay en .NET un modificador g (global) sino que se accede a la siguiente coincidencia llamando a NextMatch() o directamente utilizando el método Matches en vez de Match.

var matches = regex.Matches(input);
foreach (Match match in matches)
{
//...
}

Además de coincidencias (Match) y grupos (Group) también aparecen las capturas (Capture). Una captura se produce cuando un grupo tiene un cuantificador y por ende se captura n veces. Por defecto, cuando preguntamos por el valor de un grupo nos devuelve el valor de su última captura, pero esto podría no servirnos si las capturas de un grupo tienen distintos valores. Por ejemplo:

image

Otra cosa acerca de los grupos es que además de poner acceder a ellos por un índice también podemos darles un nombre con la sintaxis ?<nombre>

var pattern = @"(?<name>.+)@(?<twitter>.+)";
var regex = new Regex(pattern);
var input = "sergio @panicoenlaxbox\ncarmen @panicoenel20";
var matches = regex.Matches(input);
foreach (Match match in matches)
{
Console.WriteLine("Match - Value {0} Index {1} Length {2}", match.Value, match.Index, match.Length);
Group group = match.Groups["name"];
Console.WriteLine("Group - Value {0} Index {1} Length {2}", group.Value, group.Index, group.Length);
group = match.Groups["twitter"];
Console.WriteLine("Group - Value {0} Index {1} Length {2}", group.Value, group.Index, group.Length);
}

Alrededor de los grupos gira mucho de las capacidades de la expresiones regulares, por ejemplo las backing references permiten hace mención a un grupo previamente capturado con \número_de_grupo. En el siguiente ejemplo, tanto el primer caracter como el último tienen que ser iguales:

image

Incluso sería más sencillo si le damos un nombre al grupo y después lo referenciamos con \k<nombre_de_grupo>

(?<delimiter>.{1}).+\k<delimiter>{1}

También existe el concepto de no capturar un grupo. Es decir, necesitamos paréntesis para aplicar un cuantificador o cualquier otro modificador pero no queremos que se produzca una captura del grupo. Esto lo conseguimos con lo que se denomina un “non-capturing group”

(?:pattern)

Con la opción ExplicitCaptures lo que conseguimos es que no se capture ningún grupo excepto aquellos que tengan nombre..

Algo que también crea mucha confusión es que un cuantificador es greedy por defecto (avaricioso). Esto significa que consumirá la mayor cantidad posible de texto para satisfacer al cuantificador. A veces justamente queremos lo contrario, que consuma sólo lo mínimo e imprescindible para que el cuantificador sea cumplido. Para indicar a un cuantificador que sea Lazy hay que utilizar ? a continuación del cuantificador.

El ejemplo canónico para ver el tema de greedy vs lazy es capturar en un grupo la etiqueta de apertura y cierre de un elemento HTML.

  • Con la expresión <.+> y para el texto <p>Sergio</p> habrá un match que será todo el texto “<p>Sergio</p>”. Esto es porque el cuantificador + del punto “encaja” con todo el texto y sólo se detiene en el último caracter para satisfacer el > final.
  • Sin embargo con <.+?> tendremos 2 matches. “<p>” y “</p>”. El operador lazy le dice la expresión regular que tiene que parar en el primer > que encontró (el del cierre de la apertura de la etiqueta).

Otra posibilidad que nos dan los grupos es la de utilizarlos para reemplazar texto con el método Replace. En este método podemos hacer mención a un grupo capturado con $número_de_grupo o ${nombre_de_grupo}. Por ejemplo (y utilizando ahora el plugin Regular Expression Tester Extension.

image

La verdad es que las expresiones regulares dan para muchos posts, pero como decía al comienzo, tampoco quiero excesivos problemas y me basta con saber lo justo y necesario :)

Un saludo!