IDataErrorInfo y ASP.NET MVC 2

La interfaz IDataErrorInfo nos ofrece la posibilidad de generar errores personalizados y poder mostrarlos en la interfaz de usuario de nuestra aplicación. Cuando creamos una vista de manera automática con ASP.NET MVC,  está preparada para mostrar estos errores gracias a los siguientes elementos:

  • ValidationSummary: nos permite mostrar todos los errores producidos a modo de resumen en la vista.
  • ValidationMessage: Muestra cada error de forma particular, enlazando el mismo con un control de nuestra interfaz.
  • ModelState: se encargará de recopilar todos los errores producidos en nuestro objeto.

Si generamos una vista de tipo Create o Edit, podemos ver ValidationSummary al comienzo y ValidationMessage en cada uno de los controles.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IDataErrorInfoMVC.Models.Post>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>
        Create</h2>

    <% using (Html.BeginForm())        {%>
    <%: Html.ValidationSummary(true) %>

<fieldset>

<legend>Fields</legend>


<div>
            <%: Html.LabelFor(model => model.Name) %>
        </div>


<div>
            <%: Html.TextBoxFor(model => model.Name) %>
            <%: Html.ValidationMessageFor(model => model.Name) %>
        </div>


<div>
            <%: Html.LabelFor(model => model.PermanentLink) %>
        </div>


<div>
            <%: Html.TextBoxFor(model => model.PermanentLink) %>
            <%: Html.ValidationMessageFor(model => model.PermanentLink) %>
        </div>


<div>
            <%: Html.LabelFor(model => model.Tags) %>
        </div>


<div>
            <%: Html.TextBoxFor(model => model.Tags) %>
            <%: Html.ValidationMessageFor(model => model.Tags) %>
        </div>


<div>
            <%: Html.LabelFor(model => model.Date) %>
        </div>


<div>
            <%: Html.TextBoxFor(model => model.Date) %>
            <%: Html.ValidationMessageFor(model => model.Date) %>
        </div>



            <input type="submit" value="Create" />
        

    </fieldset>

    <% } %>

<div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

Para mostrar un ejemplo, voy a implementar IDataErrorInfo en la clase Post para controlar la creación y edición de nuevas entradas.

using System;
using System.ComponentModel;

namespace IDataErrorInfoMVC.Models
{
    public class Post : IDataErrorInfo
    {
        public string Name { get; set; }
        public string PermanentLink { get; set; }
        public string Tags { get; set; }
        public DateTime? Date { get; set; }

        public string this[string columnName]
        {
            get
            {
                var result = string.Empty;

                switch (columnName)
                {
                    case "Name":
                        {
                            if (string.IsNullOrWhiteSpace(Name))
                                result = "Name is required";
                            break;
                        }
                    case "PermanentLink":
                        {
                            if (string.IsNullOrWhiteSpace(PermanentLink))
                                result = "We need a permanent link";
                            break;
                        }
                    case "Date":
                        {
                            if (Date.HasValue)
                            {
                                if (Date.Value == DateTime.MinValue)
                                    result = "We need a real date";
                            }
                            else
                                result = "Date is required";
                        }
                        break;
                }
                return result;
            }
        }

        public string Error
        {
            get { return string.Empty; }
        }
    }
}

Como nuestra clase debe heredar de IDataErrorInfo, es necesario implementar sus miembros:

  • System.ComponentModel.IDataErrorInfo.this[string] : Con este miembro obtendremos el nombre de la columna que vamos a comprobar si es requerida o debe cumplir algún requesito especial. En mi caso, he utilizado un switch comprobando solamente aquellas propiedades que me parecieron importantes y retornamos el mensaje de error para aquellos casos en los cuales no cumpla la validación.
  • System.ComponentModel.IDataErrorInfo.Error: Se utiliza para devolver un mensaje de error general por el cual el objeto no ha pasado la validación. En este caso estamos devolviendo únicamente los casos particulares y, para no reflejar un error general, devolvemos Empty.

Por último, por parte del controlador, es necesario comprobar si el objeto bindeado desde nuestro formulario de creación ha pasado las validaciones establecidas ¿Cómo lo hacemos? Así de sencillo 🙂

using System.Web.Mvc;
using IDataErrorInfoMVC.Models;

namespace IDataErrorInfoMVC.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();
        }

        [HttpGet]
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(Post post)
        {
            if (ModelState.IsValid)
            {
                //validation was succeed ... do anything
                return RedirectToAction("Index");
            }
            return View();
        }
    }
}

Si intentamos crear un nuevo post sin los campos requeridos obtendríamos la siguiente imagen:

IDataErrorInfo-Validacion

¡Saludos!