c# - net - mvc 5 with entity framework
¿Cómo guardo las reglas de validación dentro de la entidad mientras uso un modelo de vista dentro del controlador? (4)
Así que tengo una entidad en mi directorio de Models
:
public class Event
{
public int Id { get; set; }
[Required, MaxLength(50), MinLength(3)]
public string Name { get; set; }
[Required, MaxLength(2000)]
public string Description { get; set; }
}
y quiero exponerlo a las vistas usando viewModel:
public class BaseEventViewModel
{
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
public class EventCreateViewModel : BaseEventViewModel
{
}
Mi razonamiento detrás de esto es que quiero que se realice toda la validación de datos en la entidad, y que todo el material de presentación (como la representación de un área de texto) se realice en el modelo de visualización. Entonces puedo usar la cantidad de modelos de vista que quiera para representar mi entidad, mientras mantengo la integridad de los datos.
Así que cambié mi controlador para usar el nuevo modelo de vista:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(EventCreateViewModel viewModel)
{
if (ModelState.IsValid)
{
db.Events.Add(new Event
{
Name = viewModel.Name,
Description = viewModel.Description
});
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel);
}
Sin embargo, no se realiza ninguna validación de la entidad y puedo enviar un formulario en blanco que genera una excepción DbEntityValidationException
.
Presumiblemente esto se debe a que ModelState.IsValid
está trabajando en el modelo de vista, no en la entidad que representa el modelo de vista. ¿Cómo puedo detectar estos errores de validación?
De hecho, encontré la respuesta después de ser empujado en la dirección correcta. Si agrego esta anotación a mi modelo de vista, heredará todas las anotaciones aplicadas a las propiedades en mi entidad:
[MetadataType(typeof(Event))]
public class BaseEventViewModel
{
public int Id { get; set; }
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
Ahora cuando envío un formulario en blanco me muestran los errores de validación como siempre.
Esto viene con la advertencia de tener que redefinir cada propiedad nuevamente dentro de mi modelo de vista, lo que de alguna manera vence el modelo de punto de vista que solo contiene las propiedades que necesita, sin embargo, funciona para mi caso.
El modelo es todo lo que se pasa a través de los parámetros del método de acción. Lo siento: la única forma de lograr eso es agregar anotaciones de datos también en ViewModels
.
EDITAR: podría agregarse en el tiempo de ejecución con reflexión (para que los cambios en la validación de la Entity
puedan ser "vistos" automáticamente en ViewModels
); sin embargo, eso es mucho trabajo. Tendría que heredar de DataAnnotationsModelValidatorProvider
, obtener todos los atributos de las propiedades apropiadas de la clase de Entity
y agregarlos a viewmodels. Creo que la mejor manera es escribir pruebas unitarias para las reglas de validación Entity
y Viewmodels
(son los mismos atributos agregados a los campos en Entity
y ViewModel
) para evitar diferentes errores de validadores.
La segunda buena manera (y la más rápida) para resolver este tipo de problema es utilizar el marco de trabajo AOP
como PostSharp
. Cree un aspecto como: EntityNameValidatorAspect
(para agregar anotaciones de datos para propiedades con valores de atributos adecuados). Luego, debe agregar este aspecto ( [EntityNameValidatorAspect]
) antes de la propiedad name en Entity
y Viewmodel
y así sucesivamente. Esa es una analogía de la refactorización del código repetido al método: simplemente "refactoriza" algunos atributos comunes en uno solo.
En este caso, sus reglas de validación deben estar en el modelo de vista. El trabajo que estás haciendo es la entrada del usuario de validación, y eso es lo que estás usando para recibir la entrada.
Podría tener un paso de validación por separado que se realice en la entidad antes de escribirla en la base de datos. En ese caso, usaría un mecanismo de validación separado para manejar las reglas en la entidad.
He estado usando este sorprendente nuget que hace anotaciones dinámicas ExpresiveAnnotations usando un patrón similar a AOP
Podrías validar cualquier lógica que puedas soñar:
public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }