SledgeHammer SledgeHammer - 1 month ago 21
C# Question

Asp.Net Core Model Validation *Multiple* Attributes

I have a Asp.Net Core REST service and I'm using the built in validation. I needed some additional functionality, so I found some examples of validation attributes that I needed, so here is a small part of my model:

[RequiredIfEmpty("B")]
[RequiredIfEmpty("C")]
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }


So, pretty obvious what I'm going for. I want to validate that A is specified if B or C is empty.

When I send a JSON request that will fail validation, I only get:

"A is required when B is empty."


I'm expecting to get:

"A is required when B is empty."
"A is required when C is empty."


So, it seems like the validation code does a distinct on the attributes based on type because it ignores the 2nd one. This is further proven if I do:

[RequiredIfEmpty("B")]
[RequiredIfEmpty2("C")]
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }


RequiredIfEmpty2 is just derived from RequiredIfEmpty, no additional code. Now I get the expected:

"A is required when B is empty."
"A is required when C is empty."


In this example, I only have 2 dependent properties, so no biggie to create a 2 version, but its very hacky and I don't like it.

I thought about changing the RequiredIfEmpty attribute to take a string[] of properties, but it doesn't appear like the MVC infrastructure would allow multiple error strings returned by a single attribute.

I did report it to Microsoft, but wondering if anybody else can think of a work-around besides having a 2 version?

Answer

You haven't provided the code for the attribute, but my validation-foo sense is telling me that the attribute is not overriding the TypeId property. You need to make sure that you're setting it to a new object in the constructor:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public sealed class RequiredIfEmptyAttribute : RequiredAttribute
{
    public RequiredIfEmptyAttribute()
    {
        TypeId = new object();
    }

    public override object TypeId { get; }

    // all the rest of the code
}

Without this, validation for multiple attributes (of the same type) will stop when the a single validation result is found. More information about the intended use of the TypeId property can be found in the MSDN documentation.

I thought about changing the RequiredIfEmpty attribute to take a string[] of properties, but it doesn't appear like the MVC infrastructure would allow multiple error strings returned by a single attribute.

Correct. You cannot return more than one message per attribute. You could implement the IValidatableObject interface to achieve this, however.