Cómo visualizar el valor de EntityValidationErrors en Visual Studio

Quizás en algún desarrollo te has enfrentado a este error:

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

El error nos indica que tenemos que visualizar la colección “EntityValidationErrors” para encontrar los errores que está marcando nuestra aplicación.

La forma más sencilla de hacerlo es correr la aplicación en “Debug mode” en Visual Studio, y en la ventanita “Watch” añadir lo siguiente:

((System.Data.Entity.Validation.DbEntityValidationException) $exception).EntityValidationErrors

Y verás los errores de esta forma:

errorvalidation

Espero que te sirva.

Otro artículo relacionado.

Cómo enviar un valor temporal de una página a otra utilizando TempData

Tratando de utilizar “ViewBag” dentro de un “Controller”, tenía lo siguiente:

ViewBag.HayErrores = "verdadero";
return RedirectToAction("OtraAccion");

En la página “OtraAccion” el valor de “ViewBag.HayErrores” ya no existe.
Esto se debe a que los “ViewBag” no son persistentes al realizar la redirección.

Para utilizar un valor temporal que sobreviva a la redirección, debes utilizar “TempData”, para nuestro ejemplo quedaría así:

TempData["HayErrores"] = "verdadero";
return RedirectToAction("OtraAccion");

Y después en tu “View” puedes checar el valor utilizando “Razor”. Solamente tienes que checar primero que el valor no sea nulo, de esta forma:

var HayErrores = "";
if (TempData["HayErrores"] != null) 
{
   HayErrores = TempData["HayErrores"].ToString();
}
if (HayErrores == "verdadero") 
{
   <h3> Error </h3>
   Hay un error, por favor vuelve a intentarlo.
}

New transaction is not allowed because there are other threads running in the session

Estaba guardando varios registros en un ciclo utilizando “Entity Framework”, y dentro de este ciclo estaba llamando “SaveChanges”.

Algo similar a esto:

foreach (var cliente in clientes)
{
   var ElCliente = new Cliente();
   ElCliente.Nombre = cliente.Nombre;
   ElCliente.Apellido = cliente.Apellido;
   repositorioClientes.Insertar(ElCliente);
   repositorioClientes.SaveChanges();
}

Al hacer lo anterior, me aparecía este mensaje:

New transaction is not allowed because there are other threads running in the session.

Investigando en Internet me di cuenta de que para evitar este error lo mejor es hacer solamente las inserciones dentro del ciclo, pero dejar el “SaveChanges” fuera del mismo, de esta forma:

foreach (var cliente in clientes)
{
   var ElCliente = new Cliente();
   ElCliente.Nombre = cliente.Nombre;
   ElCliente.Apellido = cliente.Apellido;
   repositorioClientes.Insertar(ElCliente);
}
repositorioClientes.SaveChanges();

Y listo, con eso se fue el error y los registros se guardaron correctamente.

Cómo validar un ViewModel completo utilizando IValidatableObject

Cuando tienes un “ViewModel”, puedes utilizar estas anotaciones en cada propiedad:

[Required(ErrorMessage = "Requerido")]
public string Nombres { get; set; }

Sin embargo, también puedes especificar validaciones conformadas por reglas más complicadas que utilicen varias propiedades.

Por ejemplo, validar que el parámetro de un URL contenga únicamente letras, minúsculas y guiones y que además este URL no esté asociado ya con otro registro en la base de datos.

Para realizar la validación en todo el “ViewModel”, haz lo siguiente.

Haz que la clase herede de “IValidatableObject”, de esta forma:

public class MiViewModel : IValidatableObject

Después crea este método dentro de “MiViewModel”:

public IEnumerable Validate(ValidationContext validationContext)

Y dentro de él coloca tus reglas.

Para el ejemplo que mencioné arriba, el método completo quedaría así:

public IEnumerable Validate(ValidationContext validationContext)
 {
    // # Que haya solamente letras minusculas, números y guiones en el URL
    bool BanderaURL = Regex.IsMatch(URL, @"^[a-z0-9-]+$");
    if (Regex.IsMatch(URL, @"^[a-z0-9-]+$") == false)
    {
       yield return new ValidationResult("El URL contiene caracteres no válidos.", new[] { "URL" });
    }
 // # Que no exista previamente el URL que quieren colocar
    if (miRepositorio.ChecarSiYaExisteElURL(URL) == true)
    {
       yield return new ValidationResult("El URL ya está asociado con otro registro.", new[] { "URL" });
    }
 }

El valor que regresamos con “ValidationResult” en la línea que comienza con “yield” recibe primero la descripción del error, y después un arreglo con las propiedades que están generando dicho error. En nuestro caso es solamente una.

Si la información ingresada por el usuario no pasa la validación, entonces los mensajes que estableciste aparecerán en donde coloques este código en tu “View” (.cshtml):

@Html.ValidationMessageFor(model => model.URL, "", new { @class = "help-block text-danger" })

Nota que nos estamos refiriendo a la propiedad “URL” del “model”, que coincide con la que utilizamos arriba en la línea que empieza con “yield”. Si no coinciden, no aparecerá el mensaje de error.

Y también podrás evaluarlas en tu código con:

if (ModelState.IsValid)

Utiliza “Local” en Entity Framework para buscar registros que aún no han sido guardados en la base de datos

Considera este código:

var alumno = new Alumno();
alumno.Nombre = "Juan Luna";
context.Alumnos.Add(alumno);
//No vamos a usar "context.SaveChanges" en este ejemplo
var buscarAlumno = context.Alumnos.SingleOrDefault(a => a.Nombre == "Juan Luna");

Como no guardamos el registro, entonces “buscarAlumno” será nula porque no se encontró el alumno “Juan Luna”.

Esto puede parecer confuso porque lo acababamos de insertar, pero en nuestro código la búsqueda está yendo a buscar el registro en la base de datos, y como no hemos utilizado “SaveChanges” entonces el registro está en memoria, pero no en la base de datos.

Si quieres buscar el registro localmente, cambia la línea donde buscas al alumno por la siguiente, agregando “Local”:

var buscarAlumno = context.Alumnos.Local.SingleOrDefault(a => a.Nombre == "Juan Luna");

Listo, ahora sí se encontrará al alumno.

Esto suele ser útil cuando estás guardando muchos registros de golpe en un ciclo antes de ejecutar “SaveChanges” al final.