简体   繁体   中英

How can I use the Data Validation Attributes in C# in a non-ASP.net context?

I'd like to use the data validation attributes in a library assembly, so that any consumer of the data can validate it without using a ModelBinder (in a console application, for instance). How can I do it?

Actually this is pretty cool. I used it in a WFP validation implementation recently. Most people end up writing lots of code using reflection to iterate the attributes, but there's a built in function for this.

var vc = new ValidationContext(myObject, null, null);
return Validator.TryValidateObject(myObject, vc, null, true);

You can also validate attributes on a single named property. You can also optionally pass in a list in order to access the error messages :

var results = new List<ValidationResult>();
var vc = new ValidationContext(myObject, null, null) { MemberName = "UserName"};
var isValid = Validator.TryValidateProperty(value, vc, results);

// get all the errors
var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage);

The System.ComponentModel.DataAnnotations.ValidationAttribute classes have IsValid methods that perform the validation logic. They take an Object (the value of the field they decorate) and return true or false .

You can use these attributes, and a little reflection, to roll your own aspect-oriented validator. Pass your validator an object, and the validator will get a list of PropertyInfo s and FieldInfo s. For each of these, it can call GetCustomAttributes to look for those that inherit from ValidationAttribute , and for each of these, call IsValid , passing the value of the property or field. This can be done totally dynamically without knowing the structure of the class to be validated at design-time.

TryValidateProperty is just badly written - you have to jump through hoops to get it to work outside a Controller, and even then, if you use it twice, it will end up quietly setting ModelState to valid/invalid and stop alterring that state, and stop returning accurate results from then on.

I gave up on it and just wrote my own validator. This'll loop over any set of objects in any context and tell you if they're valid:

bool isValid = true;
var invalidFields = new List<string>();

foreach (var o in viewModels)
{
    var properties = o.GetType()
        .GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach(var prop in properties)
    {
        var attrs = prop.GetCustomAttributes(true);
        if (attrs != null)
        {
            var val = prop.GetValue(o);
            ValidationAttribute[] validatorAttrs = attrs
                .Where(a => a is ValidationAttribute)
                .Select(a => (ValidationAttribute)a).ToArray();

            foreach(var attr in validatorAttrs)
            {                       
                bool thisFieldIsValid = attr.IsValid(val);
                if (!thisFieldIsValid)
                    invalidFields.Add(prop.Name);

                isValid = isValid && thisFieldIsValid;
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM