Validating DataAnnotations on method parameters

2020-04-18 09:00发布

How are data annotation attributes to be used with method parameters? I'm expecting to do something like this, but an exception isn't thrown.

private string TestValidate([StringLength(5)] string name = "Default: throw exception")
{
    ValidationContext context = new ValidationContext(name);
    Validator.ValidateObject(name, context);
    return name;
}

Alternatively, I know this will work. However, this doesn't use the convenience of the attribute convention.

private string TestValidate(string name = "Default: throw exception")
{
    ValidationContext context = new ValidationContext(name);
    Validator.ValidateValue(name, context, new[] { new StringLengthAttribute(5) });
        return name;
}

1条回答
Summer. ? 凉城
2楼-- · 2020-04-18 09:36

If you are wanting to instrument your code, then you'd use a bit of reflection (see https://msdn.microsoft.com/en-us/library/system.reflection.parameterinfo.attributes(v=vs.110).aspx)

You can evaluate your reflection once on class initialization, but this is how the reflection code would work:

// NOTE: nameof() is a .NET 4.6 feature.  If you are using an older
// version of .NET, you'll have to write the string yourself.
MethodInfo method = GetType().GetMethod(nameof(TestValidate), typeof(string));
// NOTE: it's on you to validate this and use the version with binding flags for
// special cases like non-public and static methods

ParameterInfo parameter = method.GetParameters().First();
// This is shorthand: GetCustomAttributes returns object[], you'll have
// convert that properly and reference your customAttributes later
ValidationAttribute[] customAttributes = parameter.GetCustomAttributes(
        typeof(ValidationAttribute), true); // second parameter ignored

// At this point you can handle the rest of your validation
ValidationContext context = new ValidationContext(name);
Validator.ValidateValue(name, context, customAttributes);
    return name;

Many validation frameworks simply encapsulate all the reflection necessary to get at the validation attributes. NOTE: in this case I used the subclass of ValidationAttribute as opposed to the more specific StringLengthAttribute. The call to GetCustomAttributes() will return any attribute that extends the base class we asked for. That allows you to completely change the attributes you have on your method and add more constraints without changing the code at all. You'll have to make some changes if you evaluate more than one parameter or remove a parameter.

You could make this a bit more generic as the code from the time you have a specific MethodInfo object would be generally the same. I'd probably make a couple changes in that case:

public static void Validate(MethodInfo method, params object[] values)
{
    ValidationContext context = new ValidationContext(method.Name);
    ParameterInfo[] parameters = method.GetParameters();

    for(int i = 0; i < Math.Min(parameters.Length, values.Length); i++)
    {
        // This is shorthand: GetCustomAttributes returns object[], you'll have
        // convert that properly and reference your customAttributes later
        ValidationAttribute[] customAttributes = parameter[i].GetCustomAttributes(
                typeof(ValidationAttribute), true); // second parameter ignored

        Validator.ValidateValue(values[i], context, customAttributes);
    }
}
查看更多
登录 后发表回答