Validación de la interfaz IDataErrorInfo (C#)
por Stephen Walther
Stephen Walther indica cómo mostrar mensajes de error de validación personalizados mediante la implementación de la interfaz IDataErrorInfo en una clase de modelo.
El objetivo de este tutorial es explicar un enfoque para realizar la validación en una aplicación de ASP.NET MVC. Aprenderá a evitar que alguien envíe un formulario HTML sin proporcionar valores para los campos de formulario necesarios. En este tutorial, aprenderá a realizar una validación mediante la interfaz IErrorDataInfo.
Supuestos
En este tutorial, utilizaré la base de datos MoviesDB y la tabla de base de datos Movies. Esta tabla consta de las siguientes columnas:
Nombre de la columna | Tipo de datos | Permitir valores NULL |
---|---|---|
Identificador | Int | False |
Título | Nvarchar(100) | False |
Director | Nvarchar(100) | False |
DateReleased | DateTime | False |
En este tutorial, uso Microsoft Entity Framework para generar mis clases de modelo de base de datos. La clase Movie generada por Entity Framework se muestra en la figura 1.
Figura 01: La entidad Movie (haga clic para ver la imagen a tamaño completo)
Nota:
Si desea más información sobre el uso de Entity Framework para generar las clases de modelo de base de datos, consulte mi tutorial titulado Creación de clases de modelo con Entity Framework.
Clase de controlador
Utilizamos el controlador Home para enumerar películas y crear nuevas películas. El código de esta clase se encuentra en la Lista 1.
Lista 1: Controllers\HomeController.cs
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
private MoviesDBEntities _db = new MoviesDBEntities();
public ActionResult Index()
{
return View(_db.MovieSet.ToList());
}
public ActionResult Create()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Movie movieToCreate)
{
// Validate
if (!ModelState.IsValid)
return View();
// Add to database
try
{
_db.AddToMovieSet(movieToCreate);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
}
La clase de controlador Home de la lista 1 contiene dos acciones Create(). La primera acción muestra el formulario HTML para crear una nueva película. La segunda acción Create() realiza la inserción real de la nueva película en la base de datos. La segunda acción Create() se invoca cuando se envía al servidor el formulario mostrado por la primera acción Create().
Observe que la segunda acción Create() contiene las siguientes líneas de código:
// Validate
if (!ModelState.IsValid)
return View();
La propiedad IsValid devuelve false cuando se produce un error de validación. En ese caso, se vuelve a mostrar la vista Create que contiene el formulario HTML para crear una película.
Creación de una clase parcial
Entity Framework genera la clase Movie. Puede ver el código de la clase Movie si expande el archivo MoviesDBModel.edmx en la ventana Explorador de soluciones y abre el archivo MoviesDBModel.Designer.cs en el Editor de código (vea la figura 2).
Figura 02: El código de la entidad Movie (haga clic para ver la imagen a tamaño completo)
La clase Movie es una clase parcial. Esto significa que podemos agregar otra clase parcial con el mismo nombre para ampliar la funcionalidad de la clase Movie. Agregaremos nuestra lógica de validación a la nueva clase parcial.
Agregue la clase de la lista 2 a la carpeta Models.
Lista 2: Models\Movie.cs
using System.Collections.Generic;
using System.ComponentModel;
namespace MvcApplication1.Models
{
public partial class Movie
{
}
}
Observe que la clase de la lista 2 incluye el modificador partial. Los métodos o propiedades que agregue a esta clase forman parte de la clase Movie generada por Entity Framework.
Incorporación de métodos parciales OnChanging y OnChanged
Cuando Entity Framework genera una clase de entidad, Entity Framework agrega métodos parciales a la clase automáticamente. Entity Framework genera métodos parciales OnChanging y OnChanged que corresponden a cada propiedad de la clase.
En el caso de la clase Movie, Entity Framework crea los métodos siguientes:
- OnIdChanging
- OnIdChanged
- OnTitleChanging
- OnTitleChanged
- OnDirectorChanging
- OnDirectorChanged
- OnDateReleasedChanging
- OnDateReleasedChanged
Se llama al método OnChanging justo antes de cambiar la propiedad correspondiente. Se llama al método OnChanged justo después de cambiar la propiedad.
Puede aprovechar estos métodos parciales para agregar lógica de validación a la clase Movie. La clase Movie de actualización de la lista 3 comprueba que a las propiedades Title y Director se les asignan valores no vacíos.
Nota:
Un método parcial es un método definido en una clase que no es necesario implementar. Si no implementa un método parcial, el compilador quita la firma del método y todas las llamadas al método para que no haya ningún costo en tiempo de ejecución asociado al método parcial. En el Editor de Visual Studio Code, puede agregar un método parcial escribiendo la palabra clave partial seguida de un espacio para ver una lista de parciales que se van a implementar.
Lista 3: Models\Movie.cs
using System.Collections.Generic;
using System.ComponentModel;
namespace MvcApplication1.Models
{
public partial class Movie : IDataErrorInfo
{
private Dictionary<string, string> _errors = new Dictionary<string, string>();
partial void OnTitleChanging(string value)
{
if (value.Trim().Length == 0)
_errors.Add("Title", "Title is required.");
}
partial void OnDirectorChanging(string value)
{
if (value.Trim().Length == 0)
_errors.Add("Director", "Director is required.");
}
}
}
Por ejemplo, si intenta asignar una cadena vacía a la propiedad Title, se asigna un mensaje de error a un diccionario denominado _errors.
En ese momento, en realidad no pasa nada cuando asigna una cadena vacía a la propiedad Title, y se agrega un error al campo _errors privado. Es necesario implementar la interfaz IDataErrorInfo para exponer estos errores de validación a la plataforma de ASP.NET MVC.
Implementación de la interfaz IDataErrorInfo
La interfaz IDataErrorInfo forma parte de .NET Framework desde la primera versión. Esta interfaz es una interfaz muy sencilla:
public interface IDataErrorInfo
{
string this[string columnName] { get; }
string Error { get; }
}
Si una clase implementa la interfaz IDataErrorInfo, el marco de ASP.NET MVC usará esta interfaz al crear una instancia de la clase. Por ejemplo, la acción Create() del controlador Home acepta una instancia de la clase Movie:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Movie movieToCreate)
{
// Validate
if (!ModelState.IsValid)
return View();
// Add to database
try
{
_db.AddToMovieSet(movieToCreate);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
El marco de ASP.NET MVC crea la instancia de la película que se pasa a la acción Create() mediante un enlazador de modelos (DefaultModelBinder). El enlazador de modelos es responsable de crear una instancia del objeto Movie enlazando los campos de formulario HTML a una instancia del objeto Movie.
DefaultModelBinder detecta si una clase implementa o no la interfaz IDataErrorInfo. Si una clase implementa esta interfaz, el enlazador de modelos invoca el indizador IDataErrorInfo.this para cada propiedad de la clase. Si el indizador devuelve un mensaje de error, el enlazador de modelos agrega este mensaje de error al estado del modelo automáticamente.
DefaultModelBinder también comprueba la propiedad IDataErrorInfo.Error. Esta propiedad está pensada para representar errores de validación no específicos de propiedades asociados a la clase. Por ejemplo, es posible que quiera aplicar una regla de validación que dependa de los valores de varias propiedades de la clase Movie. En ese caso, devolvería un error de validación de la propiedad Error.
La clase Movie actualizada de la lista 4 implementa la interfaz IDataErrorInfo.
Lista 4: Models\Movie.cs (implementa IDataErrorInfo)
using System.Collections.Generic;
using System.ComponentModel;
namespace MvcApplication1.Models
{
public partial class Movie : IDataErrorInfo
{
private Dictionary<string, string> _errors = new Dictionary<string, string>();
partial void OnTitleChanging(string value)
{
if (value.Trim().Length == 0)
_errors.Add("Title", "Title is required.");
}
partial void OnDirectorChanging(string value)
{
if (value.Trim().Length == 0)
_errors.Add("Director", "Director is required.");
}
#region IDataErrorInfo Members
public string Error
{
get
{
return string.Empty;
}
}
public string this[string columnName]
{
get
{
if (_errors.ContainsKey(columnName))
return _errors[columnName];
return string.Empty;
}
}
#endregion
}
}
En la lista 4, la propiedad indexer comprueba la colección _errors para ver si contiene una clave que corresponda al nombre de propiedad que se pasa al indizador. Si no hay ningún error de validación asociado a la propiedad, se devuelve una cadena vacía.
No es necesario modificar el controlador Home de ninguna manera para utilizar la clase Movie modificada. La página que aparece en la Figura 3 muestra lo que sucede cuando no se especifica ningún valor para los campos de formulario Title o Director.
Figura 03: Un formulario con valores que faltan (haga clic para ver la imagen a tamaño completo)
Observe que el valor DateReleased se valida automáticamente. Como la propiedad DateReleased no acepta valores NULL, DefaultModelBinder genera un error de validación para esta propiedad automáticamente cuando no tiene un valor. Si desea modificar el mensaje de error de la propiedad DateReleased, debe crear un enlazador de modelos personalizado.
Resumen
En este tutorial, ha aprendido a utilizar la interfaz IDataErrorInfo para generar mensajes de error de validación. En primer lugar, hemos creado una clase Movie parcial que amplía la funcionalidad de la clase Movie parcial generada por Entity Framework. A continuación, hemos agregado lógica de validación a los métodos parciales OnTitleChanging() y OnDirectorChanging() de la clase Movie. Por último, hemos implementado la interfaz IDataErrorInfo para exponer estos mensajes de validación en el marco de ASP.NET MVC.