1

In a project I have a set of validator objects, of which the Validate yield returns a ValidationResult for each error. In turn, these are collected and an exception is thrown. I've stolen this from here: Validation: How to inject A Model State wrapper with Ninject?

When an object which needs to be validated has complex properties which also need to be validated, I end up with clunky code like this (my problem is the foreach loop):

public class MembershipValidator
{

    public override IEnumerable<ValidationResult> Validate(Membership entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        foreach (var result in userValidator.Validate(entity.User))
        {
            yield return result;
        }

    }
}

Where the UserValidator in this case also has several yield return statements (of which one is shown here):

public class UserValidator
{
    public override IEnumerable<ValidationResult> Validate(User entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException();
        }

        if (entity.BirthDate == DateTime.MinValue)
        {
            yield return new ValidationResult("User", "BirthDate is mandatory");
        }

    }
}

Is there any way to write the code in the "parent" validator more concise? It's now riddled with foreach loops. Plainly writing the following code builds, but doesn't execute:

userValidator.Validate(entity.User);

And the following doesn't compile:

return userValidator.Validate(beschouwing.User);
Community
  • 1
  • 1
Aage
  • 5,932
  • 2
  • 32
  • 57
  • 1
    Why you can't return `IEnumerable`? I.e. `return userValidator.Validate(entity.User);` – Sergey Berezovskiy Aug 14 '14 at 08:19
  • @SergeyBerezovskiy See updated question, sorry I didn't make that clear. It gives me the following error: `Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.` Or am I missing something? – Aage Aug 14 '14 at 08:51
  • Can you give sample code with "parent" foreach? Btw refactoring questions usually asked on http://codereview.stackexchange.com/ – Sergey Berezovskiy Aug 14 '14 at 08:53
  • @SergeyBerezovskiy Updated question. Thanks for the info, I'll go there next time. – Aage Aug 14 '14 at 09:06

1 Answers1

0

Just split parameters check (i.e. throwing exceptions) and yielding validation results in two methods:

public override IEnumerable<ValidationResult> Validate(User entity)
{
    if (entity == null)        
        throw new ArgumentNullException();        

    return UserValidationIterator(entity);
}

private IEnumerable<ValidationResult> UserValidationIterator(User user)
{
    if (entity.BirthDate == DateTime.MinValue)
       yield return new ValidationResult("User", "BirthDate is mandatory");

    // other yields here
}

You can use same approach with membership validation.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • But this doesn't get rid of the `foreach` loop in the `Membership` validator, does it? (I added the classes to both validators for clarity). – Aage Aug 14 '14 at 11:17