How does one do property validation of a C# class using Data Annotations in .NET Framework 3.5?
Asked Answered
V

3

9

Is there a way in the .NET Framework to hand some method or validator an object instance whose class is decorated with Data Annotations, and receive a collection of errors?

I see that there is a way to do this in .NET 4.x. But is there a similar mechanism in .NET 3.5?

Varmint answered 24/6, 2013 at 22:10 Comment(1)
i don't know if there is something like that in .NET 3.5 but if you are willing to use another library, Microsoft's Enterprise Application Block contains a Validation Application Block that contains a Validator class that can validate objects decorated with DataAnnotations.Nitrometer
N
12

With a bit of reflection, you could build your own validator which scans the ValidationAttributes on the properties you have. It may not be a perfect solution, but if you're limited to using .NET 3.5, this seems like a lightweight solution, hopefully you get the picture.

static void Main(string[] args)
{

    Person p = new Person();
    p.Age = 4;

    var results = Validator.Validate(p);

    results.ToList().ForEach(error => Console.WriteLine(error));

    Console.Read();
}       

// Simple Validator class
public static class Validator
{
    // This could return a ValidationResult object etc
    public static IEnumerable<string> Validate(object o)
    {
        Type type = o.GetType();
        PropertyInfo[] properties = type.GetProperties();
        Type attrType = typeof (ValidationAttribute);

        foreach (var propertyInfo in properties)
        {
            object[] customAttributes = propertyInfo.GetCustomAttributes(attrType, inherit: true);

            foreach (var customAttribute in customAttributes)
            {
                var validationAttribute = (ValidationAttribute)customAttribute;

                bool isValid = validationAttribute.IsValid(propertyInfo.GetValue(o, BindingFlags.GetProperty, null, null, null));

                if (!isValid)
                {
                    yield return validationAttribute.ErrorMessage;
                }
            }
        }
    }
}

public class Person
{
    [Required(ErrorMessage = "Name is required!")]
    public string Name { get; set; }

    [Range(5, 20, ErrorMessage = "Must be between 5 and 20!")]
    public int Age { get; set; }
}

This prints out the following to the Console:

Name is required!
Must be between 5 and 20!

Nuptial answered 24/6, 2013 at 22:55 Comment(1)
Yes. This works because the data annotation attributes already implement an IsValid method. Thanks!Varmint
E
7

Linq Version

public static class Validator
{
    public static IEnumerable<string> Validate(object o)
        {
            return TypeDescriptor
                .GetProperties(o.GetType())
                .Cast<PropertyDescriptor>()
                .SelectMany(pd => pd.Attributes.OfType<ValidationAttribute>()
                                    .Where(va => !va.IsValid(pd.GetValue(o))))
                                    .Select(xx => xx.ErrorMessage);
        }
    }
Evulsion answered 17/12, 2014 at 14:33 Comment(0)
P
0

Those data annotation stuff mainly works in context of another framework eg. MVC w/ Razor, Fluent etc. Without another framework the annotations are just that, they're marker code, and will require a framework/extra code to do the interpretation.

Annotation by itself is not true AOP/Intercept, and hence the annotations do nothing until the object decorated with annotation is submitted to an intermediary framework that knows how to interpret / parse the marker codes (usually via. reflection).

For true AoP that could make the annotations work intrinsically you will need something like PostSharp/Unity etc. These frameworks modify the IL at run/compile time and reroutes the original code.

Pointdevice answered 24/6, 2013 at 23:8 Comment(1)
The data is not coming from a UI; it's coming from an external source that I don't control. Were that not the case, I could simply use the validation that comes with Winforms or ASP.NET MVC. I don't think I need code-injection-style AOP; see Patrick McGee's Answer.Varmint

© 2022 - 2024 — McMap. All rights reserved.