3

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?

1 Answer 1

2

.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.

Sign up to request clarification or add additional context in comments.

5 Comments

I based my attributes on the MVC Foolproof open source project, and I looked at that code real quick and they were not overriding TypeId, so I guess you found the problem :). Hopefully. I will test tomorrow. Thanks!
@SledgeHammer Glad to help. You might want to double-check on how the TypeId property is set. I'm using a new object, but MSDN uses a GUID. (Not sure if it matters.)
Wait... I'm confused. ValidationAttribute doesn't have a TypeId property? That seems like part of .Net 4.x rather then Core?
Yup... Core doesn't have that property. I then tried to override GetHashCode() with base.GetHashCode() ^ DependentProperty.GetHashCode(), but that didn't seem to work. But I got it to work by overriding Equals and checking if the DependentProperties are equal.
@SledgeHammer Holy buckets, you're right! I've learned something new today. I went ahead and updated my answer to reflect this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.