0

I have a class that contains a Dictionary<int, object> property that is marked as required. When I deserialize the data coming into the controller onto that class, the Required attribute works to prevent nulls from entering that property, but it does not stop nulls from entering the dictionary as a value given the key value pairs are formatted and passed in correctly.

Is there a way to have the Required attribute also prevent nulls from being values in the dictionary? Or is there another attribute that can be added to that property to accomplish this?

Or would the best way to solve this be to roll my own class that essentially consists of key-value pairs that I can mark both the key property and value property as Required? EX:

public class Example
{
    [Required]
    public int Key;

    [Required]
    public object Value;
}

And then just have an IEnumerable<Example> instead of Dictionary<int, object>?

B. Witter
  • 564
  • 6
  • 19
  • Might be better to write your own attribute that does the checks you want. – Neil Mar 06 '19 at 22:11
  • Possible duplicate of [How do I use IValidatableObject?](https://stackoverflow.com/questions/3400542/how-do-i-use-ivalidatableobject) – Progman Mar 06 '19 at 22:20

2 Answers2

0

The best I can think of is to have ISet<Example> (using a HashSet<Example>) with Example's GetHashCode and Equals methods overridden. That should satisfy your second desirable. As for the [Required] attribute, you would have to write the code yourself to check if those properties are not null before adding it into the ISet<Example>. That may require some reflection logic.

Dandré
  • 2,053
  • 3
  • 18
  • 38
0

This is what I ended up going with, and it works exactly how I wanted Required to work.

[AttributeUsage(AttributeTargets.Property)]
public class DictionaryRequiredAttribute : ValidationAttribute
{
    public DictionaryRequiredAttribute() : base(() => "The {0} field is required and cannot contain null values.") { }

    public override bool IsValid(object value)
    {
        if (value == null)
        {
            return false;
        }

        if (value is IDictionary dictValue)
        {
            foreach (var key in dictValue.Keys)
            {
                if (dictValue[key] == null)
                {
                    return false;
                }
            }
        }

        return true;
    }
}

Based mainly on the implementation of the RequiredAttribute found here.

B. Witter
  • 564
  • 6
  • 19
  • 1
    FYI: Just a little optimization. Instead of enumerating over the keys `dictValue.Keys` and looking up the values, you could directly enumerate over the values `dictValue.Values` (without requiring additional lookups) and condense the foreach loop into a simple and more readable Linq query: `return dictValue.Values.All(v => v != null);` –  Mar 07 '19 at 00:03