Search code examples
c#.netasp.net-corevalidationattribute

Why is "memberNames" parameter in "ValidationResult" is a collection type?


I understand, this is how we develop our custom validation attribute in ASP.NET:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class GreaterThanZeroIntegerValidationAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(Object? value, ValidationContext validationContext)
    {
        if (value is Int32 id && id > 0)
            return ValidationResult.Success;

        var memberNames = new List<String>{ validationContext.MemberName! };
        var errorMessage = String.Format("Invalid {0}.", validationContext.MemberName!);
        return new ValidationResult(errorMessage, memberNames);  //code in question
    }
}

In the code statement return new ValidationResult(errorMessage, memberNames);, I am passing a list of member names, even though it has only a single item.

I also, tried using this validation on 2 properties of a view-model:

public class OrderViewModel
{
    [GreaterThanZeroIntegerValidation]
    public Int32 Id { get; set; }

    [GreaterThanZeroIntegerValidation]
    public Int32 SomeOtherInt32Property { get; set; }

    //Other properties with different validation attributes
}

when the validation failed for both of them, this custom validation attribute was called twice, separately.

Here is the comment in the ValidationResult source code: This list of member names is meant to be used by presentation layers to indicate which fields are in error.

I understood the meaning of this statement but was unable to understand - why need a list of member names, when validation attribute is called every time for every property it is used on? Or I am interpreting the use of this parameter in a completely wrong way, I am not sure.

Question - What is the exact reason of having memberNames parameter of public ValidationResult(string? errorMessage, IEnumerable<string>? memberNames) as collection type and not singular/non-collection? Also, if possible, please provide a real world scenario that am I missing here.


Solution

  • The memberNames parameter in the ValidationResult constructor is designed to handle scenarios where a validation error is not specific to a single member but may apply to multiple members of an object. It allows you to indicate which members are in error when returning a ValidationResult object.

    While a validation attribute is called for each property it is applied to, there are cases where a validation error may span across multiple properties. For example, imagine a scenario where you have a form with multiple fields, and you want to validate a combination of values across those fields. In such cases, you can use the memberNames parameter to indicate that the error is not specific to a single property but applies to multiple properties.

    Here's an example to illustrate this:

    public class CustomValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // Check if the values of two properties are valid together
            var property1Value = ...; // Get the value of property 1
            var property2Value = ...; // Get the value of property 2
    
            if (!IsValidCombination(property1Value, property2Value))
            {
                var memberNames = new[] { "Property1", "Property2" };
                return new ValidationResult("Invalid combination of values.", memberNames);
            }
    
            return ValidationResult.Success;
        }
    }
    

    In this example, the CustomValidationAttribute validates a combination of values from two properties (Property1 and Property2). If the combination is invalid, it creates a ValidationResult object with an error message and provides the names of both properties in the memberNames parameter.