SledgeHammer
SledgeHammer

Reputation: 7690

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?

Upvotes: 3

Views: 2641

Answers (1)

Will Ray
Will Ray

Reputation: 10879

.Net Core + .Net Framework

You can override the Equals and GetHashCode methods in the attribute to create distinctions between AllowMultiple attributes.

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

    public override bool Equals(object obj) => _instance.Equals(obj);

    public override int GetHashCode() => _instance.GetHashCode();

    // all the rest of the code
}

.Net Framework Only

If you're only targeting the .Net Framework, you can use the TypeId property to be a unique identifier between two attributes of the same type. (MSDN documentation)

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

    // all the rest of the code
}

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.

Upvotes: 2

Related Questions