Validaciones con Data Annotations en MVC en .Net

Posted on Posted in .Net, Entity Framework, MVC

Si ya tienes tu proyecto creado en MVC usando Entity Framework eligiendo DataBase-First, es decir que creaste tus modelos en C# a partir de tus tablas en tu base de datos, es tiempo de crear las validaciones con Data Annotations para los modelos en MVC.

Que son los DataAnnotations?

Los DataAnnotations son decoradores que se ponen justo arriba de las propiedades de nuestras entidades, estos decoradores nos ayudan a realizar la validación tanto del lado del servidor en C# como del lado del cliente en JS, sin escribir nada mas que estas validaciones con Data Annotations.

Agregando los decoradores a mi entidad

Preservando los cambios

Antes de empezar, quiero decir que aunque los DataAnnotations van arriba de cada propiedad que queramos validar, yo normalmente creo una clase por cada entidad que quiero validar dentro de un folder llamado Metadata adentro de la carpeta Models; a esta clase le voy a meter todas las propiedades de la entidad que voy a validar.

Por ejemplo, si voy a validar la entidad User, la clase dentro de Metadata se va a llamar MetaUser y tendrá todas las propiedades de la clase User.

Para que hago esto? Porque cada vez que se actualizo o agrego alguna tabla en la base de datos todas las entidades se sobre-escriben de nuevo, aunque no tengan cambios, y esto hace que todo el código que hayamos agregado desaparecerá y tendremos que escribirlo de nuevo. Y de esta manera solamente tenemos que ligar la clase User con la clase MetaUser para que las validaciones de la clase MetaUser sean validas para la clase User tambien.

Metadata
Carpeta Metadata para preservar las validaciones

Creando la meta clase (Opcional – No recomendado si tienes control de versión)

Así es como actualmente luce mi clase User, este es el código generado por Entity Framework al conectarlo con mi base de datos en SQL Server

namespace MiProyectoMVC.Models
{
    using System;
    using System.Collections.Generic;
	
    public partial class User
    {
        public User() { }
    
        public int ID { get; set; }
        public int RoleID { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public string Salt { get; set; }
        public string FBtoken { get; set; }
        public bool IsActive { get; set; }
        public string Phone { get; set; }
        public System.DateTimeOffset RecordDate { get; set; }
    
        public virtual Role Role { get; set; }
    }
}

Lo que haré sera crear prácticamente la misma clase dentro del folder Metadata, es decir copiare las propiedades (si, un simple Ctrl + C y un Ctrl + V) y la clase se llamara MetaUser. Una vez que la haya creado solo hace falta ligarla, agregando un namespace y el decorador MetadataType a la clase original, de tal forma que quedaría mas o menos así:

namespace MiProyectoMVC.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations; // Agregar esto de aqui

    [MetadataType(typeof(MetaUser))]             // Y esto de aca
    public partial class User
    {
        public User() { }
		
		// Propiedades
    }
}

Si tienes control de versión no uses meta clases

La ventaja de hacer una meta clase es que si se actualiza la clase del modelo, no pierdes tus validaciones, solo tienes que ligarlas de nuevo.

Pero si usas control de versión (Git, TFS, etc) es mas sencillo tener las validaciones en la misma clase que genera Enitty Framework, ya que si haces commit de tus validaciones antes de actualizar el modelo, puedes hacer undo para recuperar tus validaciones, y así agregar solamente las propiedades que te hagan falta (que son por las que se actualizo tu modelo) junto con sus validaciones.

Agregar los decoradores/validaciones con Data Annotations

Hay validaciones con Data Annotations, yo solamente mostrare algunos:

  • [Display(Name = “Perfil”)]
    Cambia el nombre mostrado de la propiedad, en todas las vistas, es decir en el index, create, edit, etc
  • [Required(ErrorMessage = “El campo {0} es obligatorio”)]
    Hace que la propiedad sea requerida, mostrando el mensaje escrito en ErrorMessage, el {0} es para que jale automáticamente el nombre de la propiedad, es decir, no necesitamos escribir el nombre de la propiedad cada vez que ponemos esa validación en una propiedad diferente con un simple un simple Ctrl + C y un Ctrl + V bastara
  • [MaxLength(10, ErrorMessage = “El tamaño máximo de {0} es de {1} caracteres”)]
    Delimita el tamaño máximo del valor de la entidad, el {0} sirve para lo mismo que en la validación de arriba, y el {1} sirve para jalar el tamaño máximo declarado, en este caso 10. También existe el MinLength
  • [EmailAddress(ErrorMessage = “El campo {0} debe de un correo valido”)]
    Valida que el valor de la entidad tenga un formato de correo electrónico valido

Aplicando esos decoradores/validaciones con Data Annotations a mis propiedades quedan de la siguiente manera:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace MiProyectoMVC.Models
{
    public class MetaUser
    {
        [Key]
        public int ID { get; set; }

        [Display(Name = "Perfil")]
        [Required(ErrorMessage = "El campo {0} es obligatorio")]
        public int RoleID { get; set; }

        [Display(Name = "Nombre")]
        [Required(ErrorMessage = "El campo {0} es obligatorio")]
        [MaxLength(10, ErrorMessage = "El tamaño máximo de {0} es de {1} caracteres")]
        public string Name { get; set; }
        
        [Display(Name = "Correo Electronico")]
        [Required(ErrorMessage = "El campo {0} es obligatorio")]
        [MaxLength(250, ErrorMessage = "El tamaño maximo de {0} es de {1} caracteres")]
        [EmailAddress(ErrorMessage = "El campo {0} debe de un correo valido")]
        public string Email { get; set; }

        //	Otras propiedades
		
    }
}

El resultado

Cuando vallamos a crear un nuevo User en Users/Create, y presionemos el botón de agregar veremos lo siguiente

Validaciones con Data Annotations 1
Validaciones con Data Annotations 1

Como vemos nos obliga a insertar los datos antes de enviar el formulario de regreso al servidor, ademas de que los nombres que muestra a la izquierda son los que definimos en el decorador Display.

Si ponemos valores que no cumplan las otras dos validaciones nos muestra esto

Validaciones con Data Annotations 2
Validaciones con Data Annotations 2

Ahora nos esta validando que realmente la propiedad nombre tenga como máximo 10 caracteres y que el correo electrónico tenga un formato valido.

Todo esto tambien se valida del lado del servidor en el controlador… sin escribir nada… automáticamente…:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,RoleID,Name,Email,Password,Salt,FBtoken,IsActive,Phone,RecordDate")] User user)
{
	// Si no cumple con alguna validación entonces ModelState.IsValid sera FALSE
	// y el error se regresara en la entidad y se mostrara en la interfaz gráfica al cliente
	if (ModelState.IsValid) 
	{
		db.Users.Add(user);
		db.SaveChanges();
		return RedirectToAction("Index");
	}

	ViewBag.RoleID = new SelectList(db.Roles, "ID", "Name", user.RoleID);
	return View(user);
}

Conclusiones

Realmente es muy conveniente tener esta herramienta, ya que hay validaciones que aunque son simples, tenemos que hacerlas, y se hace muuuy tedioso tener que escribir las mismas validaciones por proyecto, por entidad y por propiedad de entidad.

En caso de que estés haciendo tu aplicación para varios idiomas, buenas noticias para ti, los Data Annotations lo soportan de maravilla, en este post explico como hacer los Data Annotations multilenguaje.

Otro beneficio es que se realiza la validación tanto en el servidor como en el navegador del cliente, asegurando dos cosas:

  • No hay que escribir nada de código de validación ni duplicar nada mas que estas validaciones con Data Annotations
  • El usuario no tiene que esperar a que se envié el formulario al servidor para recibir la retroalimentacion de que  tiene un campo mal
  • Si el usuario tiene Javascript deshabilitado del navegador o algún malicioso se salta la validación del cliente, tendremos la validación del servidor para asegurarnos de que los datos cumplan nuestros requerimientos sin escribir ningún código extra.