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!

lunes, 16 de marzo de 2015

Pdf en ASP.NET MVC - MvcRazorToPdf

Tarde o temprano, generar contenido pdf en una aplicación web es algo que nos tocará hacer.

En el caso de ASP.NET MVC, yo he optado por el paquete de Nuget MvcRazorToPdf

Esta solución se basa en iTextSharp (el port de .NET de iText) e iTextSharp XML Worker, un add-on para iText que permite convertir HTML a PDF.

Lo que agrega MvcRazorToPdf es un nuevo tipo PdfActionResult que hereda de ViewResult. De este modo, crear un documento PDF es tan sencillo como crear un método de acción que devuelva una instancia del tipo PdfActionResult (que procesará nuestra vista con normalidad y convertirá a PDF el HTML resultante).

return new PdfActionResult(model);

Actualmente parece haber un problema con el paquete de Nuget y la versión de sus dependencias. Para solucionarlo (aunque entiendo que estará solucionado en futuras versiones del paquete), bastará con ejecutar los siguientes comandos en la consola de Nuget:

Install-Package iTextSharp -version 5.5.3

Install-Package itextsharp.xmlworker -version 5.5.3

Además del ejemplo básico, PdfActionResult tiene algunas sobrecargas que pueden resultarnos muy útiles, por ejemplo hay uno donde tenemos acceso al documento que se está generando, así como al writer utilizado. Lo cierto es que no quisiera tener que lidiar mucho con la generación en bruto de PDF a través de iTextSharp, pero para algunos escenarios podría ser necesario:

return new PdfActionResult(model, (writer, document) => document.SetPageSize (PageSize.A4));

Incluso podemos especificar que directamente devuelva el PDF como un archivo para su descarga:

return new PdfActionResult(model, (writer, document) => document.SetPageSize (PageSize.A4))

{

    FileDownloadName = "Example.pdf"

};

También podemos generar ficheros PDF en el servidor (gracias a un método extensor de ControllerContext) y ya después hacer con él lo que creamos oportuno:

byte[] output = ControllerContext.GeneratePdf();

var path = Server.MapPath("~/App_Data/Order.pdf");

if (System.IO.File.Exists(path))

{

    System.IO.File.Delete(path);

}

System.IO.File.WriteAllBytes(path, output);

return File(path, "application/pdf", Path.GetFileName(path));

Si hasta aquí todo ha sido contar las bondades de MvcRazorToPdf, la parte negativa la encontramos no ya en el propio paquete, sino en su dependencia con iTextSharp XML Worker. Durante el proceso de convertir el HTML a PDF, hay perdidas…He empleado más tiempo en bregar con las rarezas de iTextSharp XML Worker, que en todo lo demás. Cierto es que encontré tarde la documentación de iTextSharp XML Worker e incluso un editor WYSIWYG, pero cosas como tener que utilizar el atributo valign en vez de la propiedad CSS vertical-align, o tener que generar para las imágenes rutas absolutas en vez de relativas, son cosas que tendrás que aprender con el manido método ensayo-error. En cualquier caso, para algo sencillo seguiré apostando por esta solución.

Un saludo!

martes, 3 de marzo de 2015

Creación de paquetes Nuget con .nuspec

Además de ser consumidor de paquetes Nuget (como cualquier otro desarrollador .NET), ahora estoy inmerso en el rol de creador.

image

Por ahora tengo 4 paquetes, pero veo que esto crecerá con el tiempo, me está gustando!

Mi primera incursión fue de la mano de Nuget Package Explorer, una herramienta GUI que permite de forma visual crear y publicar un paquete. A este respecto ya escribió un post mi compañero MookieFumi, Crear un paquete Nuget.

Sin embargo, a medida que el ritmo de publicación ha ido creciendo, el crear los paquetes de forma manual ha resultado una operativa poco eficiente. Como en todo, automatizar es un proceso que facilita nuestro trabajo.

Siguiendo la guía oficial de creación y publicación de paquetes, el proceso por el que he optado consta de los siguientes pasos:

Descargar Nuget.exe o utilizar la versión que encontramos en la carpeta .nuget cuando habilitamos la restauración de paquetes en Visual Studio.

Crear un fichero .nuspec con la información del paquete. Lo más sencillo es ejecutar
Nuget.exe spec en la misma carpeta donde se encuentre el proyecto (el fichero .csproj) a partir del cual queremos crear el paquete.

Al ejecutar el anterior comando se generará un fichero <Proyecto>.nuspec con el contenido:

<?xml version="1.0"?>

<package >

  <metadata>

    <id>$id$</id>

    <version>$version$</version>

    <title>$title$</title>

    <authors>$author$</authors>

    <owners>$author$</owners>

    <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>

    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>

    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>

    <requireLicenseAcceptance>false</requireLicenseAcceptance>

    <description>$description$</description>

    <releaseNotes>Summary of changes made in this release of the package.</releaseNotes>

    <copyright>Copyright 2015</copyright>

    <tags>Tag1 Tag2</tags>

  </metadata>

</package>

Lo más importante son las variables:

  • $id$. Nombre del ensamblado
  • $version$. Versión según AssemblyVersionAttribute
  • $title$. Título según AssemblyTitle
  • $author$. Autor según AssemblyCompanyAttribute
  • $description$. Descripción según AssemblyDescriptionAttribute

El valor de todas ellas se pueden establecer desde las propiedades del proyecto (Properties > Application > Assembly Information).

En relación al resto, personalmente prescindo de licenseUrl, iconUrl y releaseNotes, y actualizo el valor de projectUrl (con la url del repositorio de github) y tags (para su posterior búsqueda desde el registro de Nuget).

Después de editarlo, deberemos incluir el fichero .nuspec a nuestro VCS favorito.

A partir de aquí (y teniendo la precaución de ir incrementado la versión del ensamblado cada vez que queramos publicar una nueva versión del paquete), podemos utilizar el siguiente comando para generar el fichero .nupkg

Nuget.exe pack <Proyecto>.csproj -Prop Configuration=Release -Build -OutputDirectory .\bin\Release

  • -Prop Configuration=Relase indica que queremos empaquetar en esa configuración (por defecto lo hace en Debug, aunque editando el fichero .csproj se podría cambiar… todo esto está explicado en la guía).
  • -Build indica que queremos compilar antes de empaquetar
  • -OutputDirectory indica donde se creará el fichero .nupkg

El fichero .nupkg tendrá el nombre $id$<version>.nupkg

Aunque ahora podríamos también publicar a través de línea de comandos con Nuget.exe, aquí yo prefiero publicar manualmente con Nuget Package Explorer.

Lógicamente, el fichero .nupkg no se sube al VCS de turno, cualquier .gitignore que se precie lo tendrá excluido.

Si extraemos con Nuget Package Explorer los metadatos del paquete que acabamos de crear veremos algo así:

<?xml version="1.0" encoding="utf-8"?>

<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">

    <metadata>

        <id>MongoDBTempData</id>

        <version>1.0.0.0</version>

        <title>MongoDBTempData</title>

        <authors>panicoenlaxbox</authors>

        <owners>panicoenlaxbox</owners>

        <projectUrl>https://github.com/panicoenlaxbox/MongoDBTempData</projectUrl>

        <requireLicenseAcceptance>false</requireLicenseAcceptance>

        <description>ASP.NET MVC TempData MongoDB provider</description>

        <copyright>Copyright 2015</copyright>

        <tags>ASP.NET MVC TempData MongoDB</tags>

        <dependencies>

            <dependency id="Microsoft.AspNet.Mvc" version="5.2.3" />

            <dependency id="mongocsharpdriver" version="1.10.0" />

            <dependency id="Newtonsoft.Json" version="6.0.8" />

        </dependencies>

    </metadata>

    <files>

        <file src="lib\net45\MongoDBTempData.dll" target="lib\net45\MongoDBTempData.dll" />

    </files>

</package>

Como podemos ver, las variables han sido reemplazadas y además se han incluido como dependencias las propias dependencias Nuget que tuviera el proyecto (esto podría gustarte o no, eso lo dejo a tu elección).

Dando un pasito más allá (y ahora sí entrando en los dominios de la automatización), para la creación automática del paquete he utilizado un evento post-compilación. Cierto es que podría haber optado por un fichero MSBUILD, pero no me importa que cada vez que compilo en Release se genere un nuevo paquete, sobre todo porque la publicación (como he dicho antes) la haré de forma manual.

El código MSBUILD a incluir en el evento post-build es:

if $(ConfigurationName) == Release $(SolutionDir)\.nuget\NuGet.exe pack "$(ProjectPath)" -Prop Configuration=Release

De este modo, mi flujo de creación y publicación de paquetes Nuget queda así:

  • Crear fichero .nuspec con Nuget.exe, editarlo y subirlo al VCS.
  • Agregar código MSBUILD en el evento post-build para que se genere automáticamente el fichero .nupkg si compilo en Release.
  • Publicar manualmente el paquete cuando proceda.

Un saludo!