I have this custom validation attribute for validating a collection. I need to adapt this to work with IEnumerable. I tried making the attribute a generic, but you can't have a generic attribute.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class CollectionHasElements : System.ComponentModel.DataAnnotations.ValidationAttribute
{
public override bool IsValid(object value)
{
if (value != null && value is IList)
{
return ((IList)value).Count > 0;
}
return false;
}
}
I'm having trouble casting it to an IEnumerable so that I can check its count() or any().
Any ideas?
Try this
var collection = value as ICollection;
if (collection != null) {
return collection.Count > 0;
}
var enumerable = value as IEnumerable;
if (enumerable != null) {
return enumerable.GetEnumerator().MoveNext();
}
return false;
or, since C# 7.0 with pattern matching:
if (value is ICollection collection) {
return collection.Count > 0;
}
if (value is IEnumerable enumerable) {
return enumerable.GetEnumerator().MoveNext();
}
return false;
Note: Testing for ICollection.Count
is more efficient than getting an enumerator and beginning to enumerate the enumerator. Therefore I try to use the Count
property whenever possible. However, the second test would work alone, since a collection always implements IEnumerable
.
The inheritance hierarchy goes like this: IEnumerable > ICollection > IList
. IList
implements ICollection
and ICollection
implements IEnumerable
. Therefore IEnumerable
will work for any well designed type of collection or enumeration but not IList
. For instance Dictionary<K,V>
does not implement IList
but ICollection
and therefore also IEnumeration
.
The .NET naming conventions say that an attribute class name should always end in "Attribute". Therefore your class should be named CollectionHasElementsAttribute
. When applying the attribute you can drop the "Attribute" part.
[CollectionHasElements]
public List<string> Names { get; set; }