Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you apply validation attributes to objects in collections?

Basically, if I have a collection of objects, how can I apply a validation attribute to each item in the collection (such as MaxLengthAttribute)?

public class Foo
{
    public ICollection<string> Bars { get; set; }
}

For example, how can I ensure that Bars contains strings that validate against a max length of 256?

Update:

I understand how to apply a validation attribute on a single property, but the question is asking how to apply it on objects within a collection.

public class Foo
{
    [StringLength(256)] // This is obvious
    public string Bar { get; set; }

    // How do you apply the necessary attribute to each object in the collection!
    public ICollection<string> Bars { get; set; }
}
like image 644
myermian Avatar asked Nov 03 '22 07:11

myermian


2 Answers

I know this question is kinda old, but maybe someone comes along looking for answers.

I'm not aware of a generic way to apply attributes to collection items, but for the specific string length example I used the following:

public class StringEnumerationLengthValidationAttribute : StringLengthAttribute
{
    public StringEnumerationLengthValidationAttribute(int maximumLength)
        : base(maximumLength)
    { }

    public override bool RequiresValidationContext { get { return true; } }
    public override bool IsValid(object value)
    { return false; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var e1 = value as IEnumerable<string>;
        if (e1 != null) return IsEnumerationValid(e1, validationContext);
        return ValidationResult.Success; // what if applied to something else than IEnumerable<string> or it is null?
    }

    protected ValidationResult IsEnumerationValid(IEnumerable<string> coll, ValidationContext validationContext)
    {
        foreach (var item in coll)
        {
            // utilize the actual StringLengthAttribute to validate the items
            if (!base.IsValid(item) || (MinimumLength > 0 && item == null))
            {
                return new ValidationResult(base.FormatErrorMessage(validationContext.DisplayName));
            }
        }
        return ValidationResult.Success;
    }
}

Apply as follows, to require 4-10 characters for each collection item:

[StringEnumerationLengthValidation(10, MinimumLength=4)]
public ICollection<string> Sample { get; set; }
like image 75
grek40 Avatar answered Nov 14 '22 05:11

grek40


OK I found a pretty nice article explaining some useful info about this:

http://blogs.msdn.com/b/codeanalysis/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt-t-gt-instead-of-list-lt-t-gt-david-kean.aspx

Here is some suggested code that would make the Bars member of Foo do what you want.

public class Foo
{
    public ValidatedStringCollection Bars = new ValidatedStringCollection(10);
}

public class ValidatedStringCollection : Collection<string>
{

    int _maxStringLength;

    public ValidatedStringCollection(int MaxStringLength)
    {
        _maxStringLength = MaxStringLength;
    }

    protected override void InsertItem(int index, string item)
    {
        if (item.Length > _maxStringLength)
        {
            throw new ArgumentException(String.Format("Length of string \"{0}\" is beyond the maximum of {1}.", item, _maxStringLength));
        }
        base.InsertItem(index, item);
    }

}

class Program
{
    static void Main(string[] args)
    {
        Foo x = new Foo();
        x.Bars.Add("A");
        x.Bars.Add("CCCCCDDDDD");
        //x.Bars.Add("This string is longer than 10 and will throw an exception if uncommented.");

        foreach (string item in x.Bars)
        {
            Console.WriteLine(item);
        }

        Console.ReadKey();
    }
}

The linked article has several suggestions including overriding the other methods on the collection, implementing events conditionally, etc. This should hopefully cover you.

like image 26
NYCdotNet Avatar answered Nov 14 '22 04:11

NYCdotNet