5.5.3 Alternativa para validar reglas con ef core

Existe otra manera más fácil para realizar validaciones personalizadas a nuestras tablas utilizando las clases proporcionadas por .NET

Puedes crear tus reglas personalizadas creando una clase que herede de la clase ValidationAttribute e implementar el método IsValid esta función recibe 2 parámetros un objeto llamado value que contiene el valor del campo que queremos validar y un objeto validationContext que nos sirve para manejar la validación de la regla.

Agregamos un índice único a nuestra tabla producto, en nuestra carpeta Models luego Entity Configurations luego nuestro archivo ProductoConfiguration.cs

ProductoConfiguration.cs
public class ProductoConfiguration
: IEntityTypeConfiguration<Producto>
{
public void Configure(EntityTypeBuilder<Producto> builder)
{
//Código indice para el nombre en producto
builder.HasIndex(e => e.Nombre)
.IsUnique()
.HasName("UX_ProductoNombre");
}
}

Creamos la migración y actualizamos nuestra base de datos

En nuestra clase productoDAO agregamos una función para validar que el nombre del producto no se repita, recibe como parámetros el Id del producto y el nombre del producto

ProductoDAO.cs
public bool EsNombreRepetido(int id, string nombre)
{
var registroRepetido = contexto.Producto
.FirstOrDefault(c => c.Nombre == nombre
&& c.Id != id);
if (registroRepetido != null)
{
customError = new CustomError(400, String.Format(
this.localizacion.GetLocalizedHtmlString("Repeteaded"),
"Producto", "nombre"), "Nombre");
return true;
}
return false;
}

En nuestra carpeta Rules > Producto creamos una nueva clase NombreValidation.cs que hereda de la clase ValidationAttribute y sobrescribimos el método ValidationResult

NombreValidation.cs
public class NombreValidation : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
//Obtenemos el contexto de nuestra aplicación
var contexto = (CaducaContext)validationContext
.GetService(typeof(CaducaContext));
//Obtenemos la clase para personalizar los mensajes de
//error por idioma
var localizacion = (LocService)validationContext
.GetService(typeof(LocService));
//Obtenemos el producto a agregar/modificar
Producto producto = (Producto)validationContext
.ObjectInstance;
ProductoDAO productoDAO = new ProductoDAO(contexto,
localizacion);
//En lugar de producto.Nombre también puedes pasar
//value.toString() el cual contiene el valor del campo
//a validar
//if (productoDAO.EsNombreRepetido(producto.Id,
// value.ToString())
if (productoDAO.EsNombreRepetido(producto.Id,
producto.Nombre))
{
//Si el producto esta repetido regresamos el
//mensaje de error
return new ValidationResult(productoDAO.customError
.Message);
}
//Indica que la regla se cumple correctamente
return ValidationResult.Success;
}
}

Quitamos el código para validar el nombre de nuestra clase ProductoDAO

A nuestra clase Producto.cs le agregamos la validación

Producto.cs
public class Producto
{
//..... Los demás campos de tu tabla producto van aqui
[NombreValidation()]
public string Nombre { get; set; }
}

Esta forma es más sencilla que la anterior, la anterior nos permitirá probar las reglas de forma individual de una forma más fácil, como ejercicio puedes agregar la validación para la clave repetida.

Puedes pasar como parámetro el nombre de algún campo adicional que necesites, por ejemplo

Producto.cs
public class Producto
{
//..... Los demás campos de tu tabla producto van aqui
[NombreValidation("Id")]
public string Nombre { get; set; }
}
NombreValidation.cs
public class NombreValidation : ValidationAttribute
{
string campoAdicional;
public NombreValidation(string campoAdicional)
{
this.campoAdicional = campoAdicional;
}
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
//...código
var campo = validationContext.ObjectType.GetProperty(
campoAdicional);
if (campo == null)
throw new ArgumentException("Propiedad no encontrada");
var id = (int)campo.GetValue(
validationContext.ObjectInstance);
//...código
}
}

Ejercicio de práctica:

Agrega un índice y la validación para el campo clave, puedes comparar tu solución con mi código en github.