Asignar Atributos de Validación De la Entidad de Dominio a DTO


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 puedes ver, he inventado estos atributos completamente. Qué marco de validación (NHibernate Validator, DataAnnotations, ValidationApplicationBlock, Castle Validator, etc.) está en uso aquí no es importante.

En mi capa de cliente, también tengo una configuración estándar donde no uso las entidades de Dominio en sí mismas, sino que las mapeo 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 sea capaz de realizar algunas validaciones básicas a 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 de negocio (validación a nivel de propiedad) en la capa ViewModel (DTO).

Entonces, ¿qué se puede hacer?

Asumiendo que utilizo una herramienta de automatización como AutoMapper para mapear mis entidades de dominio a mis DTOs de ViewModel, ¿no sería genial transferir de alguna manera la lógica de validación para las propiedades mapeadas a ViewModel también?

Las preguntas son:

1) ¿Es 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 aportación!

Author: Martin Suchanek, 2010-01-16

7 answers

Si está utilizando algo que soporte las anotaciones de datos, 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 el MetadataTypeAttribute tanto en la entidad de dominio como en DTO:

[MetadataType(typeof(ProductMetadata))]
public class Product

Y

[MetadataType(typeof(ProductMetadata))]
public class ProductViewModel

Esto no funcionará fuera de la caja con todos los validadores - es posible que tenga que ampliar su marco de validación de elección para implementar un enfoque similar.

 11
Author: Sam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-11 08:31:34

El propósito de la validación es asegurarse de que los datos que entran en su aplicación cumplan con ciertos criterios, con eso en mente, el único lugar en el que tiene sentido validar restricciones de propiedad, como las que ha identificado aquí, es en el punto en el que 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 tipos de dominio y usar estos tipos de dominio en el modelo de vista donde tenga sentido. Si tener una validación más compleja (es decir, expresar reglas de negocio que requieren un mayor conocimiento que el expresado en una sola propiedad), estas pertenecen a métodos en el modelo de dominio que aplican los cambios.

En resumen, coloque los atributos de validación de datos en sus modelos de vista y déjelos fuera de sus modelos de dominio.

 9
Author: Neal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-14 21:21:29

Resulta que AutoMapper puede ser capaz de hacer esto para nosotros automáticamente, que es el mejor escenario.

AutoMapper-users: 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.

 4
Author: Martin Suchanek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-20 11:06:23

¿Por qué no utilizar una interfaz para expresar su intención? Eg:

public interface IProductValidationAttributes {
    [NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
    string Name { get; set; }

    [NotLessThan0]
    decimal Price { get; set;}
}
 3
Author: JontyMC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-16 01:52:17

Si utiliza entidades de dominio escritas a mano, ¿por qué no poner sus entidades de dominio en su propio ensamblado y usar ese mismo ensamblado tanto en el cliente como en el servidor? Puede reutilizar las mismas validaciones.

 1
Author: ASharp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-16 15:55:42

He estado considerando esto también por un tiempo. Entiendo totalmente la respuesta de Brad. Sin embargo, supongamos que quiero usar otro marco de validación que sea adecuado para anotar tanto entidades de dominio como modelos de vista.

La única solución que se me ocurre en papel que aún funciona con atributos sería crear otro atributo que "apunte" a la propiedad de una entidad de dominio que está reflejando en su modelo de vista. He aquí 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 framework como xVal podría ampliarse 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. No he tenido tiempo de darle más detalles.

¿Algún pensamiento?

 1
Author: ventaur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-17 23:45:01

En primer lugar, no hay noción de entidad de dominio "estándar". Para mí, la entidad de dominio estándar no tiene ningún configurador para empezar. Si tomas ese enfoque, puedes tener una api más significativa, que en realidad transmite algo sobre tu dominio. Por lo tanto, puede tener un servicio de aplicación que procesa su DTO, crea comandos que puede ejecutar directamente contra sus objetos de dominio, como SetContactInfo, ChangePrice, etc. Cada uno de estos puede elevar ValidationException, que a su vez puede recoger en su servicio y presentar al usuario. Aún puede dejar sus atributos en las propiedades de dto para una validación simple a nivel de atributo/propiedad. Para cualquier otra cosa, consulta tu dominio. E incluso si esta es la aplicación CRUD, evitaría exponer mis entidades de dominio a la capa de presentación.

 0
Author: epitka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-25 13:57:27