web-dev-qa-db-fra.com

DataAnnotations: validation récursive d'un graphique d'objet entier

J'ai un graphique d'objet parsemé d'attributs DataAnnotation, où certaines propriétés des objets sont des classes qui ont elles-mêmes des attributs de validation, etc.

Dans le scénario suivant:

public class Employee
{
    [Required]
    public string Name { get; set; }

    [Required]
    public Address Address { get; set; }
}

public class Address
{
    [Required]
    public string Line1 { get; set; }

    public string Line2 { get; set; }

    [Required]
    public string Town { get; set; }

    [Required]
    public string PostalCode { get; set; }
}

Si j'essaie de valider un Employee de Address sans valeur pour PostalCode, je voudrais (et j'attends) une exception, mais je n'en obtiens aucune. Voici comment je le fais:

var employee = new Employee
{
    Name = "Neil Barnwell",
    Address = new Address
    {
        Line1 = "My Road",
        Town = "My Town",
        PostalCode = "" // <- INVALID!
    }
};

Validator.ValidateObject(employee, new ValidationContext(employee, null, null));

Quelles autres options ai-je avec Validator qui garantiraient que toutes les propriétés sont validées récursivement?

54
Neil Barnwell

Ma réponse est devenue trop longue pour être mise ici, alors je l'ai transformée en article de blog :)

Validation récursive à l'aide de DataAnnotations

La solution vous permet d'obtenir une validation récursive en utilisant la même méthode de base que vous utilisez actuellement.

52
Josh

Voici une alternative à l'approche d'attribut opt-in. Je crois que cela traversera correctement le graphique d'objet et validera tout.

public bool TryValidateObjectRecursive<T>(T obj, List<ValidationResult> results) {

bool result = TryValidateObject(obj, results);

var properties = obj.GetType().GetProperties().Where(prop => prop.CanRead 
    && !prop.GetCustomAttributes(typeof(SkipRecursiveValidation), false).Any() 
    && prop.GetIndexParameters().Length == 0).ToList();

foreach (var property in properties)
{
    if (property.PropertyType == typeof(string) || property.PropertyType.IsValueType) continue;

    var value = obj.GetPropertyValue(property.Name);

    if (value == null) continue;

    var asEnumerable = value as IEnumerable;
    if (asEnumerable != null)
    {
        foreach (var enumObj in asEnumerable)
        {
            var nestedResults = new List<ValidationResult>();
            if (!TryValidateObjectRecursive(enumObj, nestedResults))
            {
                result = false;
                foreach (var validationResult in nestedResults)
                {
                    PropertyInfo property1 = property;
                    results.Add(new ValidationResult(validationResult.ErrorMessage, validationResult.MemberNames.Select(x => property1.Name + '.' + x)));
                }
            };
        }
    }
    else
    {
        var nestedResults = new List<ValidationResult>();
        if (!TryValidateObjectRecursive(value, nestedResults))
        {
            result = false;
            foreach (var validationResult in nestedResults)
            {
                PropertyInfo property1 = property;
                results.Add(new ValidationResult(validationResult.ErrorMessage, validationResult.MemberNames.Select(x => property1.Name + '.' + x)));
            }
        }
    }
}

return result;
}

Code le plus à jour: https://github.com/reustmd/DataAnnotationsValidatorRecursive

Paquet: https://www.nuget.org/packages/DataAnnotationsValidator/

De plus, j'ai mis à jour cette solution pour gérer les graphiques d'objets cycliques. Merci pour les commentaires.

36
manu08