1

I have a question regarding property validation, however, I haven't yet been able to find an appropriate answer.

I have the following classes

public class IndexViewModel
{
    public Film Film { get; set; }

    public ReviewModel Review { get; set; }
}

public class ReviewModel
{
    [RequiredIf // only fire if 'Genre' is equal to Genre.Horror]
    public string Text { get; set; }
}

public class Film
{
    public string Title { get; set; }
    public Director Director { get; set; }
    public Genre Genre { get; set; }
}

public class Director
{
    public string Name { get; set; }
}

public enum Genre
{
    Horror,
    Thriller,
    SciFi,
    Drama
}

Is it possible to add a [RequiredIf] attribute on the Text property in ReviewModel which fires validation based on the value of Genre in the Film model. Any Help would be greatly appreciated.

Grizzly
  • 5,873
  • 8
  • 56
  • 109
tbonejenkins
  • 379
  • 3
  • 17
  • Check out Foolproof Validation: https://foolproof.codeplex.com/ and FluentValidation: https://github.com/JeremySkinner/FluentValidation – solidau Apr 19 '16 at 18:35
  • No it is not possible using validation attributes because the validation context is for the `ReviewModel` only. But your `IndexViewModel` is not the correct use of a view model. It should contain the properties you need to editing as a flat structure, and should not contain properties which are data models. –  Apr 20 '16 at 00:13

2 Answers2

3

I wouldn't recommend using validation attributes when the properties that need to be validated aren't in the class it's associated with. I haven't seen a RequiredIfAttribute implementation that cuts across models like that. (Here's a good one from a different question.)

What about a simple implementation of IValidatableObject? It would be quite clear, and MVC will check for it when constructing the model.

public class IndexViewModel : IValidatableObject
{
    public Film Film { get; set; }

    public ReviewModel Review { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Film.Genre == Genre.Horror && string.IsNullOrWhiteSpace(Review.Text))
        {
            yield return new ValidationResult(
                "Please provide a review for this film.",
                new string[] { nameof(Review.Text) });
        }
    }
}
Community
  • 1
  • 1
Will Ray
  • 10,621
  • 3
  • 46
  • 61
1

Put it in your business layer.

[RequiredIf // only fire if 'Genre' is equal to Genre.Horror

]

if you need to put it on your model, you might as well implement

System.ComponentModel.IValidatableObject

and then use the ObjectValidator to validate it.

However based on experience, I do basic attribute decoration on my model such as Required and StringLength, so that asp.net MVC can pick up on, and dump the important business rule in my service layer, since the validation logic is rarely that simple and usually require external data elements like additional calls to the database.

it really depends on your use cases. I like to keep my data classes dumb because i expect the data to be moved around and thus the validation logic would be lost during serialization i.e JSON serialization.

dfdsfdsfsdf
  • 653
  • 3
  • 7