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!

sábado, 2 de mayo de 2015

Validación en Entity Framework

Si es un post anterior vimos como validar el modelo de ASP.NET MVC, en este veremos cómo validar las entidades de Entity Framework antes de que persistan en la base de datos.

Que sea o no Entity Framework quien deba validar ciertas reglas de negocio antes de grabar los cambios, es una pregunta para la que no tengo respuesta, básicamente porque no sabría esgrimir un argumento sólido ni a favor ni en contra de si es una opción válida. Imagino que en aplicaciones puramente centradas en datos podría ser una opción viable, mientras que en aplicaciones orientadas al dominio, poco menos que sería un sacrilegio mover estas validaciones a la capa de persistencia. Lo dicho, me limitaré a presentar como validar entidades con Entity Framework y dejare la discusión relacionada con la arquitectura para los que realmente saben :)

Lo primero es saber que valida Entity Framework:

  • Reglas en propiedades especificadas vía DataAnnotations o FluentAPI
  • Reglas en propiedades o entidades especificadas con el atributo CustomValidationAttribute.
  • Entidades que implementan IValidatableObject.

Aquí cabe mencionar que hay ciertos atributos de validación (atributos que heredan de ValidationAttribute) que además de validar también instruyen a Code First sobre como inferir el esquema de la base de datos. Por ejemplo [Required] validará que la propiedad tiene algún valor y además creara un campo en base de datos que no admite nulos. Otro ejemplo sería [StringLength] que crearía un campo nvarchar con una determinada longitud. Si optamos por FluentAPI, IsRequired y HasMaxLength serían sus equivalentes e igualmente validarían y servirían para inferir el esquema. 

Yo personalmente prefiero utilizar FluentAPI, pero es cierto que con los atributos tenemos una oportunidad de personalizar el mensaje de error si no se supera la validación. Además, si en vez de Code First estamos utilizando Database First, sólo nos queda la opción de los atributos usando las clases buddy para agregar validación a las clases auto-generadas.

public partial class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{
}

public class CustomerMetadata
{
[Required]
[StringLength(50)]
public string FirstName { get; set; }
}

Lo siguiente que debemos conocer es cuando se ejecutan las validaciones.

Bajo demanda se ejecutarán en los siguientes casos:

  • Validar una entidad llamando al método DbEntityEntry.GetValidationResult.
  • Validar una propiedad llamando al método DbPropertyEntry.GetValidationErrors.
  • Validar todas las entidades Added o Modified, llamando al método DbContext.GetValidationErrors.

Automáticamente:

  • Se validarán todas las entidades Added o Modified al llamar al método DbContext.SaveChanges (siempre y cuando Configuration.ValidateOnSaveEnabled sea true, que es su valor por defecto).

Validar una entidad con DbEntityEntry.GetValidationResult devuelve un objeto del tipo DbEntityValidationResult con 3 propiedades:

  • IsValid, indica si la entidad es válida.
  • ValidationErrors, colección de DbValidationError con propiedades PropertyName y ErrorMessage
  • Entry, entidad que está siendo validada.

La propiedad Entry podría parecer redundante (de hecho en el caso de DbEntityEntry.GetValidation lo es, ya sabemos cual es la entidad que estamos validando) pero en otros casos como en SaveChanges nos servirá para saber a que entidad nos estamos refiriendo cuando iteremos sobre un conjunto de entidades.

También es importante ver no es necesario que la entidad esté atachada al contexto para lanzar la validación.

var customer = new Customer();
DbEntityValidationResult validationResult = context.Entry(customer).GetValidationResult();
if (!validationResult.IsValid)
{
Console.WriteLine("La entidad de tipo {0} no es válida.", validationResult.Entry.GetType().Name);
foreach (DbValidationError validationError in validationResult.ValidationErrors)
{
Console.WriteLine("Propiedad {0}", validationError.PropertyName);
Console.WriteLine("Mensaje {0}", validationError.ErrorMessage);
}
}

En el caso de querer validar una propiedad con DbPropertyEntry.GetValidationErrors, lo que nos devolverá este método es directamente una colección de objetos del tipo DbValidationError.

var customer = new Customer();
ICollection<DbValidationError> validationErrors = context.Entry(customer).Property(p=>p.FirstName).GetValidationErrors();
if (validationErrors.Any())
{
foreach (DbValidationError validationError in validationErrors)
{
Console.WriteLine("Propiedad {0}", validationError.PropertyName);
Console.WriteLine("Mensaje {0}", validationError.ErrorMessage);
}
}

La validación con DbContext.GetValidationErrors y con DbContext.SaveChanges es muy similar, ambas validan entidades Added o Modified. La diferencia está en que DbContext.GetValidationErrores devuelve una colección de DbEntityValidationResult, mientras que SaveChanges lanza una excepción del tipo DbEntityValidationException.

var customer = new Customer();
context.Customers.Add(customer);

try
{
context.SaveChanges();
}
catch (DbEntityValidationException ex)
{
foreach (DbEntityValidationResult entityValidationError in ex.EntityValidationErrors)
{
Console.WriteLine("Entidad {0}", entityValidationError.Entry.Entity.GetType());
Console.WriteLine("Estado {0}", entityValidationError.Entry.State);
foreach (var validationError in entityValidationError.ValidationErrors)
{
Console.WriteLine("Propiedad {0}", validationError.PropertyName);
Console.WriteLine("Mensaje {0}", validationError.ErrorMessage);
}
}
}

Qué solo valide entidades Added o Modified podemos personalizado si sobrescribimos el método ShouldValidateEntity de DbContext.

Otro punto a tener en cuenta es que cuando se llama a DbEntityEntry.GetValidationResult (ya sea directamente o como consecuencia de haber llamado a DbContext.GetValidationErrors o DbContext.SaveChanges) no tendremos que preocuparnos por efectos no deseados con LazyLoading puesto que se desactivará automáticamente durante el proceso de validación.

Si hablamos de validar entidades como conjunto, hemos visto que podemos hacerlo con CustomValidationAttribute o IValidatableObject (y cabe recordar que esta validaciones a nivel de entidad sólo se ejecutaran si se superan las validaciones a nivel de propiedad), pero en ninguno de los casos expuestos hasta ahora hemos tenido acceso al contexto durante la validación, luego ¿cómo validar una entidad si tenemos que validar también datos en otras entidades del contexto que no son navegables a partir de la entidad que estamos validando o incluso tenemos que validar contra datos ya existentes en la base de datos? Pues sobrescribiendo el método ValidateEntity de DbContext donde ya sí tenemos acceso al contexto.

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
return base.ValidateEntity(entityEntry, items);
}

Las estrategias posibles aquí son dos (según he podido ver), una es utilizar la variable items que es un diccionario que se pasará a IValidatableObject. De este modo podríamos pasar el propio contexto y así en método Validate tenerlo disponible. Otra opción es llevar a cabo directamente la validación en el propio método ValidateEntity (llamando finalmente a base.ValidateEntity(entityEntry, items) si resulta oportuno).

public class ConsoleApplication1DbContext : DbContext
{
public DbSet<Customer> Customers { get; set; }

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
items["context"] = this;
return base.ValidateEntity(entityEntry, items);
}
}

public class Customer : IValidatableObject
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var context = (DbContext)validationContext.Items["context"];
//do something...
return Enumerable.Empty<ValidationResult>();
}
}

Un saludo!

lunes, 30 de marzo de 2015

Snippet para IDisposable

Si hay algún snippet que echo especialmente en falta en Visual Studio es uno para implementar el patrón IDisposable.

Quizás me haya perdido algo, pero no lo encuentro y tampoco R# me ayuda con ello.

En cualquier caso, he recordado que escribí hace tiempo un post sobre la extensión Snippet Designer y cómo te ayudaba a la hora de escribir un nuevo snippet.

Basándome en la implementación del patrón IDisposable que he encontrado en IDisposable, Done Right y utilizando la función ClassName que está disponible dentro del código de un snippet, he creado un snippet que automatiza la tarea.

Para que Visual Studio reconozca el snippet simplemente tienes que crear un fichero con la extensión .snippet y el código que te muestro a continuación, en C:\Users\<TuUsuario>\Documents\Visual Studio 2013\Code Snippets\Visual C#\My Code Snippets.

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
<Title>Disposable</Title>
<Author>panicoenlaxbox@gmail.com</Author>
<Description>IDisposable pattern</Description>
<HelpUrl>https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/</HelpUrl>
<Shortcut>disp</Shortcut>
</Header>
<Snippet>
<Declarations>
<Object Editable="false">
<ID>classname</ID>
<ToolTip>
</ToolTip>
<Default>
</Default>
<Function>ClassName()</Function>
<Type>
</Type>
</Object>
</Declarations>
<Code Language="csharp" Delimiter="$"><![CDATA[
private bool _disposed;

public void Dispose()
{
Dispose(true);
// if your class does not work with unmanaged objects,
// remove the next line and the finalize method
GC.SuppressFinalize(this);
}

~$classname$()
{
Dispose(false);
}

protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;

if (disposing)
{
// free other managed objects that implement
// IDisposable only
}

// release any unmanaged objects
// set the object references to null

_disposed = true;
}]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>

Un saludo!

jueves, 19 de marzo de 2015

Distintas opciones para validar el modelo en ASP.NET MVC

La validación del modelo en ASP.NET MVC es un característica muy importante ya que garantiza que la lógica de la entidad que recibe el controlador es conforme a la reglas de validación que hayamos definido para la entidad.

Hace tiempo publiqué un post relacionado con la validación del modelo pero estaba más centrado en cómo implementar la validación en el lado del cliente Validación en cliente en ASP.NET MVC. Sin embargo, repasando ahora las distintas opciones que tenemos disponibles para validar el modelo en ASP.NET MVC, reconozco que al ser varias no siempre es sencillo decidir cual de ellas es más apropiada según qué escenario.

Que yo sepa hay hasta 4 distintas formas de implementar la validación del modelo:

La validación del modelo es lanzada por los proveedores de validación del modelo, que inicialmente es una colección con 3 proveedores:

private void DebugModelValidatorProviders()
{
foreach (var provider in ModelValidatorProviders.Providers)
{
System.Diagnostics.Debug.WriteLine(provider.GetType().Name);
}
//DataAnnotationsModelValidatorProvider
//DataErrorInfoModelValidatorProvider
//ClientDataTypeModelValidatorProvider
}

El proveedor DataAnnotationsModelValidatorProvider es quien se encargará de llamar a CustomValidationAttribute, ValidationAttribute e IValidatableObject. Por otro lado, DataErrorInfoModelValidatorProvider llamará a IDataErrorInfo.

Sabiendo ya que opciones tenemos ¿Cuál utilizar?

Reconozco que en mi caso he usado siempre ValidationAttribute, pero intentaré en este post sopesar pros y contras de todas las opciones disponibles (así después no tendré que volver a hacer el ejercicio de reflexión si más adelante lo necesito).

Para todos los ejemplos utilizaremos un ViewModel muy sencillo:

public class PersonViewModel
{
[Required]
[Display(Name = "Nombre")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Edad")]
public int Age { get; set; }
}


Un controlador
using System.Web.Mvc;
using MyValidation.ViewModels;

namespace MyValidation.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new PersonViewModel();
return View(model);
}

[HttpPost]
public ActionResult Index(PersonViewModel model)
{
return View(model);
}
}
}

Y una vista:
@model MyValidation.ViewModels.PersonViewModel
<div class="container">
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(m => m.FirstName, new { @class = "control-label" })
@Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="form-group">
@Html.LabelFor(m => m.Age, new { @class = "control-label" })
@Html.TextBoxFor(m => m.Age, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Age)
</div>
<button type="submit" class="btn btn-default">Submit</button>
}
</div>


CustomValidationAttribute

La idea es crear un método estático por cada propiedad que se quiera validar. Si además queremos validar la entidad como conjunto, crearemos igualmente otro método estático. Después decoraremos la entidad y/o cada propiedad con el atributo CustomValidation donde especificaremos que método tiene que llamar (en el ejemplo que viene a continuación he llamado a los métodos IsValid, IsFirstNameValid y IsAgeValid pero podrían tener cualquier otro nombre).
using System.ComponentModel.DataAnnotations;

namespace MyValidation.ViewModels
{
[CustomValidation(typeof(PersonCustomValidation), "IsValid", ErrorMessage = "No eres yo")]
public class PersonViewModel
{
[CustomValidation(typeof(PersonCustomValidation), "IsFirstNameValid", ErrorMessage = "Nombre no es válido")]
[Required]
[Display(Name = "Nombre")]
public string FirstName { get; set; }
[CustomValidation(typeof(PersonCustomValidation), "IsAgeValid")]
[Required]
[Display(Name = "Edad")]
public int Age { get; set; }
}

public class PersonCustomValidation
{
public static ValidationResult IsValid(PersonViewModel person)
{
if (person.FirstName == "Sergio" && person.Age == 39)
{
return ValidationResult.Success;
}
return new ValidationResult(null);
}

public static ValidationResult IsFirstNameValid(string firstName)
{
if (firstName.IndexOf(" ") == -1)
{
return ValidationResult.Success;
}
return new ValidationResult(null);
}

public static ValidationResult IsAgeValid(int age)
{
if (age > 18)
{
return ValidationResult.Success;
}
return new ValidationResult("No eres mayor de edad");
}
}
}

Lo más relevante del código es que si devolvemos un ValidationResult con errorMessage establecido a null, tomará el especificado en el atributo CustomValidation, sino prevalecerá el que hayamos pasado a ValidationResult. También es importante ver que para que se llame al método que valida la entidad como un conjunto, tienen que haberse superado con éxito todas las validaciones de propiedades, en caso contrario no se llamará.

Lógicamente el código (no sólo de éste sino de los futuros ejemplos) es mejorable. No se valida por ejemplo si el parámetro firstName es null, etc. Por eso he metido [Required], para asegurarme no tener que escribir más código del necesario en los ejemplos :)

¿Qué no me gusta de CustomValidationAttribute?

Claramente tener que hardcodear el nombre de los métodos. Sólo por eso no lo utilizaré.

ValidationAttribute
using System.ComponentModel.DataAnnotations;

namespace MyValidation.ViewModels
{
[YouAreNotMe(ErrorMessage = "No eres yo")]
public class PersonViewModel
{
[Required]
[Display(Name = "Nombre")]
[StringWithoutSpaces(ErrorMessage = "{0} no puede tener espacios")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Edad")]
[LegalAge(ErrorMessage = "No eres mayor de edad")]
public int Age { get; set; }
}

public class StringWithoutSpacesAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value.ToString().IndexOf(" ") == -1)
{
return ValidationResult.Success;
}
return new ValidationResult(null);
}
}

public class LegalAgeAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (((int)value) > 18)
{
return ValidationResult.Success;
}
return new ValidationResult(null);
}
}

public class YouAreNotMeAttribute: ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var person = (PersonViewModel) value;
if (person.FirstName == "Sergio" && person.Age == 39)
{
return ValidationResult.Success;
}
return new ValidationResult(null);
}
}
}

¿Qué me gusta de ValidationAttribute?

  • Ganamos seguridad de tipos (sobre todo comparado con CustomValidation).
  • Mejoramos la expresividad del código.
  • Cada atributo sólo tiene una responsabilidad, no es una colección de métodos estáticos en una misma clase como con CustomValidation.
  • Mantiene una simetría con el resto de validadores que vienen de serie.

En mi opinión, es una buena opción para validar propiedades pero no tanto para validar la entidad como conjunto.

IValidatableObject

Con IValidatableObject, en vez de trabajar con atributos ahora tenemos que implementar esta interface en clase de entidad que queremos validar.

No sirve para validar propiedades de forma individual, sino que está pensada para validar la entidad como un conjunto, es decir, si queremos validar propiedades tendremos que seguir optando por alguna de las opciones que lo soportan (de las expuestas todas excepto justo IValidatableObject).
public class PersonViewModel:IValidatableObject
{
[Required]
[Display(Name = "Nombre")]
[StringWithoutSpaces(ErrorMessage = "{0} no puede tener espacios")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Edad")]
[LegalAge(ErrorMessage = "No eres mayor de edad")]
public int Age { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var result = new List<ValidationResult>();
if (FirstName == "Sergio" && Age == 39)
{
result.Add(ValidationResult.Success);
return result;
}
result.Add(new ValidationResult("No eres yo"));
if (FirstName != "Sergio")
{
result.Add(new ValidationResult("Nombre no es válido",new[] {"FirstName"}));
}
if (Age != 39)
{
result.Add(new ValidationResult("Edad no es válida", new[] { "Age" }));
}
return result;
}
}

¿Qué me gusta de IValidatableObject?

  • Qué no hay que castear un parámetro para acceder a los valores de lo que estamos validando, al estar implementado en la propia clase del modelo, podemos acceder a las propiedades de la instancia sin más.
  • Qué puede devolver una lista de ValidationResult, pudiendo así afinar (si es preciso) que propiedades contienen errores con la sobrecarga de ValidationResult que espera un IEnumerable<string> memberNames.

Por ahora (y si nadie me convence de lo contrario) es mi opción preferida para validar una entidad como conjunto.

IDataErrorInfo

Con franqueza, es para mí la mayor desconocida. No la utilizado nunca y creo no la utilizaré. Es muy distinta a cualquiera de las otras aproximaciones, no sirve para validar la entidad como un conjunto (sólo para validar propiedades), obliga a devolver un mensaje (si localizas aplicaciones sabrás que esto es un problema)… pero en cualquier caso, un ejemplo sencillo servirá para completar el post y cumplir con lo prometido.
public class PersonViewModel:IDataErrorInfo
{
[Required]
[Display(Name = "Nombre")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Edad")]
public int Age { get; set; }

public string this[string columnName]
{
get
{
if (columnName == "FirstName")
{
if (FirstName.IndexOf(" ") != -1)
{
return "Nombre no es válido";
}
}
else if (columnName == "Age")
{
if (Age < 18)
{
return "No eres mayor de edad";
}
}
return null;
}
}

public string Error { get; private set; }
}

Como conclusión, a día de hoy mi resumen es el siguiente:

  • Si tengo que validar propiedades utilizo ValidationAttribute.
  • Si tengo que validar una entidad utilizo IValidatableObject.

Un saludo!