asp.net-mvc - net - mvc required field validation
AsignaciĆ³n de atributos de validaciĆ³n desde la entidad de dominio a DTO (7)
Tengo una entidad de capa de dominio estándar:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set;}
}
que tiene algún tipo de atributos de validación aplicados:
public class Product
{
public int Id { get; set; }
[NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
public string Name { get; set; }
[NotLessThan0]
public decimal Price { get; set;}
}
Como puede ver, he inventado estos atributos por completo. ¿Qué marco de validación (NHibernate Validator, DataAnnotations, ValidationApplicationBlock, Castle Validator, etc.) en uso aquí no es importante?
En mi capa de cliente, también tengo una configuración estándar donde no utilizo las entidades del Dominio, sino que las asigno a ViewModels (también conocido como DTO) que usa mi capa de vista:
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set;}
}
Digamos entonces que quiero que mi cliente / vista pueda realizar algunas validaciones básicas de nivel de propiedad.
La única forma en que veo que puedo hacer esto es repetir las definiciones de validación en el objeto ViewModel:
public class ProductViewModel
{
public int Id { get; set; }
// validation attributes copied from Domain entity
[NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
public string Name { get; set; }
// validation attributes copied from Domain entity
[NotLessThan0]
public decimal Price { get; set;}
}
Esto claramente no es satisfactorio, ya que ahora he repetido la lógica comercial (validación de nivel de propiedad) en la capa ViewModel (DTO).
Entonces, ¿qué puede hacerse?
Suponiendo que utilizo una herramienta de automatización como AutoMapper para asignar mis entidades de Dominio a mis DTO de ViewModel, ¿no sería genial transferir de algún modo también la lógica de validación de las propiedades mapeadas al ViewModel?
Las preguntas son:
1) ¿Es esta una buena idea?
2) Si es así, ¿se puede hacer? Si no, ¿cuáles son las alternativas, si las hay?
¡Gracias de antemano por cualquier aporte!
¿Por qué no usar una interfaz para expresar tu intención? P.ej:
public interface IProductValidationAttributes {
[NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
string Name { get; set; }
[NotLessThan0]
decimal Price { get; set;}
}
El propósito de la validación es garantizar que los datos que ingresan a la aplicación cumplan con ciertos criterios, teniendo en cuenta que el único lugar en el que tiene sentido validar las restricciones de propiedad, como los que ha identificado aquí, es cuando acepta datos de una fuente no confiable (es decir, el usuario).
Puede usar algo como el "patrón de dinero" para elevar la validación a su sistema de tipo de dominio y usar estos tipos de dominio en el modelo de vista donde tenga sentido. Si tiene una validación más compleja (es decir, está expresando reglas comerciales que requieren un conocimiento mayor que el expresado en una sola propiedad), estas pertenecen a métodos del modelo de dominio que aplican los cambios.
En resumen, ponga los atributos de validación de datos en sus modelos de vista y déjelos fuera de sus modelos de dominio.
En primer lugar, no hay una noción de entidad de dominio "estándar". Para mí, la entidad de dominio estándar no tiene ningún setter para comenzar. Si toma ese enfoque, puede tener una API más significativa, que de hecho transmita algo sobre su dominio. Por lo tanto, puede tener un servicio de aplicaciones que procese su DTO, crea comandos que puede ejecutar directamente en contra de sus objetos de dominio, como SetContactInfo, ChangePrice, etc. Cada uno de estos puede generar ValidationException, que a su vez puede recopilar en su servicio y presentarlo a el usuario. Aún puede dejar sus atributos en las propiedades de dto para la validación simple de nivel de atributo / propiedad. Para cualquier otra cosa, consulte su dominio. E incluso si esta es una aplicación CRUD, evitaría exponer las entidades de mi dominio a la capa de presentación.
He estado considerando esto también desde hace un tiempo. Entiendo totalmente la respuesta de Brad. Sin embargo, supongamos que quiero utilizar otro marco de validación que sea adecuado para anotar ambas entidades de dominio y ver modelos.
La única solución que puedo encontrar en papel que todavía funciona con atributos sería crear otro atributo que "apunte" a la propiedad de una entidad de dominio que está duplicando en su modelo de vista. Aquí hay un ejemplo:
// In UI as a view model.
public class UserRegistration {
[ValidationDependency<Person>(x => x.FirstName)]
public string FirstName { get; set; }
[ValidationDependency<Person>(x => x.LastName)]
public string LastName { get; set; }
[ValidationDependency<Membership>(x => x.Username)]
public string Username { get; set; }
[ValidationDependency<Membership>(x => x.Password)]
public string Password { get; set; }
}
Un marco como xVal podría extenderse para manejar este nuevo atributo y ejecutar los atributos de validación en la propiedad de la clase de dependencia, pero con el valor de propiedad de su modelo de vista. Simplemente no he tenido tiempo de explicar esto más.
¿Alguna idea?
Resulta que AutoMapper puede hacer esto para nosotros automágicamente, que es el mejor de los casos.
Usuarios de AutoMapper: ¿Transferir atributos de validación al viewmodel?
http://groups.google.com/group/automapper-users/browse_thread/thread/efa1d551e498311c/db4e7f6c93a77302?lnk=gst&q=validation#db4e7f6c93a77302
No he podido probar las soluciones propuestas allí, pero tengo la intención de hacerlo en breve.
Si está utilizando algo que admite DataAnnotations, debería poder usar una clase de metadatos para contener sus atributos de validación:
public class ProductMetadata
{
[NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
public string Name { get; set; }
[NotLessThan0]
public decimal Price { get; set;}
}
y agregarlo en MetadataTypeAttribute en la entidad de dominio y DTO:
[MetadataType(typeof(ProductMetadata))]
public class Product
y
[MetadataType(typeof(ProductMetadata))]
public class ProductViewModel
Esto no funcionará de manera inmediata con todos los validadores; es posible que necesite ampliar su marco de validación de elección para implementar un enfoque similar.
Si usa entidades de dominio escritas a mano, ¿por qué no pone sus entidades de dominio en su propio ensamblaje y usa ese mismo ensamblaje tanto en el cliente como en el servidor? Puede reutilizar las mismas validaciones.