Dataannotations: Recursively Validating an Entire Object Graph

How can I tell the Data Annotations validator to also validate complex child properties?

You will need to make your own validator attribute (eg, [CompositeField]) that validates the child properties.

Recursive validation using annotations and IValidatableObject

See this answer: https://stackoverflow.com/a/3400627/724944

So, there is an error in your class' atributes, and therefore Validate method doesn't get called.
I suggest using CustomValidationAttribute like this:

[CustomValidation(typeof(Customer), "ValidateRelatedObject")]
public CustomerDetails Details
{
get;
private set;
}

public static ValidationResult ValidateRelatedObject(object value, ValidationContext context)
{
var context = new ValidationContext(value, validationContext.ServiceContainer, validationContext.Items);
var results = new List<ValidationResult>();
Validator.TryValidateObject(value, context, results);

// TODO: Wrap or parse multiple ValidationResult's into one ValidationResult

return result;

}

How to validate model in .Net Core 3.1 in console app when the model contains another class's object as a property using DataAnnotations?

In brief, this doesn't work because TryValidateObject doesn't support the validation of complex nested types. This was also an issue for the .NET Framework version of this type, and @reustmd created a NuGet Package to solve the problem. Unfortunately, it doesn't support .NET Core, but I found there is a package forked from this which does. From the readme:

Installation

Available as NuGet-Package dataannotationsvalidator:

Install-Package dataannotationsvalidator

Usage

See file DataAnnotationsValidator/DataAnnotationsValidator.Tests/DataAnnotationsValidatorTests.cs

Short example:

var validator = new DataAnnotationsValidator.DataAnnotationsValidator();

var validationResults = new List<ValidationResult>();

validator.TryValidateObjectRecursive(modelToValidate, validationResults);

This should solve your problem.

As an aside, I also found this proposal discussing adding this functionality to .NET. If you're interested in a technical design discussion of that proposal, there is a video posted by the .NET Foundation discussing it.

Update

For your scenario above, I've installed the above package, changed the VisitRequest class to make the provider required

public class VisitRequest
{
[Required]
public ProviderInfo Provider { get; set; }

[Required]
[MaxLength(64)]
public string PayerId { get; set; }
}

and then ran the following:

var jsonString = @"{
""ExternalVisitID"": ""123456789"",
""EVVMSID"": ""100""
}";

var modelToValidate = JsonConvert.DeserializeObject<VisitRequest>(jsonString);

var validator = new DataAnnotationsValidator.DataAnnotationsValidator();

var validationResults = new List<ValidationResult>();

validator.TryValidateObjectRecursive(modelToValidate, validationResults);

foreach (var item in validationResults)
{
Console.WriteLine(item.ErrorMessage);

foreach (var memberName in item.MemberNames)
{
Console.WriteLine($"\t{memberName}");
}
}

which produces output of:

The Provider field is required.
Provider
The PayerId field is required.
PayerId

If you're wanting to list each field of a provider that's missing individually, even though that part of the JSON is empty, I think you'll have to fork the NuGet package and make those changes. But this is probably a reason why .NET hasn't supported this functionality out of the box, because people have different expectations for how it should behave.

Validating DataAnnotations with Validator class

I found the answer here: http://forums.silverlight.net/forums/p/149264/377212.aspx

MVC recognizes the MetaDataType attribute, but other projects do not. Before validating, you need to manually register the metadata class:

TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Persona), typeof(Persona_Validation)), typeof(Persona));

ValidationContext context = new ValidationContext(p, null, null);
List<ValidationResult> results = new List<ValidationResult>();

bool valid = Validator.TryValidateObject(p, context, results, true);

Validating a collection with Dataannotations

As it seems, your grade value is not simple int, it is int with some domain specific restrictions. Following that logic, I would design new class for holding grade value

public class GradeValue
{
[Range(100, 200)]
public int Value { get; set; }
}

And in your model, property that describes list of grade values, would be

public List<GradeValue> GradeValues { get; set; }

And the validation will apply rande to every GradeValue in your viewModel. And in view, you do not have to change any single line of code. Moreover, you could design your GradeValue class to be implicitely convertible to int and vice versa, for simpler usage.



Related Topics



Leave a reply



Submit