Array must contain 1 element
Asked Answered
I

9

56

I have the following class:

public class CreateJob
{
    [Required]
    public int JobTypeId { get; set; }
    public string RequestedBy { get; set; }
    public JobTask[] TaskDescriptions { get; set; }
}

I'd like to have a data annotation above TaskDescriptions so that the array must contain at least one element? Much like [Required]. Is this possible?

Inflectional answered 13/11, 2012 at 13:25 Comment(0)
M
40

I've seen a custom validation attribute used for this before, like this:

(I've given sample with a list but could be adapted for array or you could use list)

public class MustHaveOneElementAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var list = value as IList;
        if (list != null)
        {
            return list.Count > 0;
        }
        return false;
    }
}

[MustHaveOneElementAttribute (ErrorMessage = "At least a task is required")]
public List<Person> TaskDescriptions { get; private set; }

// as of C# 8/9 this could be more elegantly done with     
public class MustHaveOneElementAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        return value is IList {Count: > 0};
    }
}

Credit to Antonio Falcão Jr. for elegance

Mirk answered 13/11, 2012 at 13:31 Comment(1)
As of C # 8/9, all of this code can be replaced by this: return value is IList {Count: > 0};Portentous
S
86

It can be done using standard Required and MinLength validation attributes, but works ONLY for arrays:

public class CreateJob
{
    [Required]
    public int JobTypeId { get; set; }
    public string RequestedBy { get; set; }
    [Required, MinLength(1)]
    public JobTask[] TaskDescriptions { get; set; }
}
Strigil answered 19/5, 2013 at 12:31 Comment(5)
Apparently it's only available in .net 4.5+ as well. :-(Darden
A little more info about this attribute: You should use [Required] in conjunction with [MinLength(1)] because MinLength will not trigger if the array is null (not empty, null). Also important to note, this is not supported (correct me if I'm wrong) with default client side validators. It will only trigger the ModelState.IsValid.Chloral
That attribute also works for objects that implements the ICollection interface and strings.Lunula
You should add @Pluc's comment to your answer. Having the [Required] attribute makes your answer better than the accepted answer above.Asti
[MinLength(1)] works with IEnumerable<T> in ASP.NET CoreSchrecklichkeit
M
40

I've seen a custom validation attribute used for this before, like this:

(I've given sample with a list but could be adapted for array or you could use list)

public class MustHaveOneElementAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var list = value as IList;
        if (list != null)
        {
            return list.Count > 0;
        }
        return false;
    }
}

[MustHaveOneElementAttribute (ErrorMessage = "At least a task is required")]
public List<Person> TaskDescriptions { get; private set; }

// as of C# 8/9 this could be more elegantly done with     
public class MustHaveOneElementAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        return value is IList {Count: > 0};
    }
}

Credit to Antonio Falcão Jr. for elegance

Mirk answered 13/11, 2012 at 13:31 Comment(1)
As of C # 8/9, all of this code can be replaced by this: return value is IList {Count: > 0};Portentous
I
7

Here is a bit improved version of @dove solution which handles different types of collections such as HashSet, List etc...

public class MustHaveOneElementAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var collection = value as System.Collections.IEnumerable;
        if (collection != null && collection.GetEnumerator().MoveNext())
        {
            return true;
        }
        return false;
    }
}
Immobility answered 1/6, 2018 at 10:10 Comment(1)
Shortened to: public override bool IsValid(object value) => value is System.Collections.IEnumerable collection && collection.GetEnumerator().MoveNext();Wrecker
F
6

Please allow me a side note on using MinLengthAttribute with .NET Core.

Microsoft recommends using Razor Pages starting with .NET Core 2.0.

Currently, The validation with MinLengthAttribute on a property within the PageModel does not work:

[BindProperty]
[Required]
[MinLength(1)]
public IEnumerable<int> SelectedStores { get; set; }

ModelState.IsValid returns true when SelectedStores.Count() == 0.

Tested with .NET Core 2.1 Preview 2.

Fistula answered 12/4, 2018 at 8:45 Comment(3)
MinLength seems not to work correctly in .NET Core 2.2 (nor with arrays, IEnumerables, ICollections, IList....)Catie
yeah, it appears to be broken on .NET 5, as well.Limburg
[MinLength(1)] attribute is working for my requirement to have a least 1 element in my List<T>, with .NET 6 Blazor.Buckles
H
4

You have to use 2 standard annotation attribute

public class CreateJob
{
    [MaxLength(1), MinLength(1)]
    public JobTask[] TaskDescriptions { get; set; }
}
Heliogravure answered 26/11, 2020 at 16:27 Comment(0)
K
2

Starting with .net 8, you can use System.ComponentModel.DataAnnotations.LengthAttribute.

This attribute now works with String AND Collection. You then provide a minimum and a maximum length to the attribute.

Usage:

[Length(1, int.MaxValue)]
public IEnumerable<int> TaskDescriptions { get; set; }
Kussell answered 11/11, 2023 at 17:8 Comment(1)
Note that validation will pass if the value is null, so you need to add a [Required] attribute as well. also, MinLength is enough for checking that the list has at least one value.Contort
B
1

Further to mynkow's answer, I've added the ability to pass a minimum count value to the attribute and produce meaningful failure messages:

public class MinimumElementsRequiredAttribute : ValidationAttribute
{
  private readonly int _requiredElements;

  public MinimumElementsRequiredAttribute(int requiredElements)
  {
    if (requiredElements < 1)
    {
      throw new ArgumentOutOfRangeException(nameof(requiredElements), "Minimum element count of 1 is required.");
    }

    _requiredElements = requiredElements;
  }

  protected override ValidationResult IsValid(object value, ValidationContext validationContext)
  {
    if (!(value is IEnumerable enumerable))
    {
      return new ValidationResult($"The {validationContext.DisplayName} field is required.");
    }

    int elementCount = 0;
    IEnumerator enumerator = enumerable.GetEnumerator();
    while (enumerator.MoveNext())
    {
      if (enumerator.Current != null && ++elementCount >= _requiredElements)
      {
        return ValidationResult.Success;
      }
    }

    return new ValidationResult($"At least {_requiredElements} elements are required for the {validationContext.DisplayName} field.");
  }
}

Use it like this:

public class Dto
{
  [MinimumElementsRequired(2)]
  public IEnumerable<string> Values { get; set; }
}
Berhley answered 2/4, 2020 at 13:2 Comment(0)
D
1

Just updating Dove's (@dove) response to C# 9 syntax:

    public class MustHaveOneElementAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
            => value is IList {Count: > 0};
    }
Delagarza answered 27/5, 2021 at 14:43 Comment(0)
R
-1

MinLength attribute considers the value as valid if it's null. Therefore just initialize your property in the model as an empty array and it'll work.

MinLength(1, ErrorMessageResourceName = nameof(ValidationErrors.AtLeastOneSelected), ErrorMessageResourceType = typeof(ValidationErrors))]
int[] SelectedLanguages { get; set; } = new int[0];
Rescission answered 15/4, 2018 at 20:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.