4

I'm attempting to use DataAnnotation attribute validation outside of an ASP.net MVC application. Ideally I'd like to take any model class in my console application and decorate it as follows:

private class MyExample
{
    [Required]
    public string RequiredFieldTest { get; set; }

    [StringLength(100)]
    public int StringLengthFieldTest { get; set; }

    [Range(1, 100)]
    public int RangeFieldTest { get; set; }

    [DataType(DataType.Text)]
    public object DataTypeFieldTest { get; set; }

    [MaxLength(10)]
    public string MaxLengthFieldTest { get; set; }
}

Then (pseudo) do something like this:

var item = new MyExample(); // not setting any properties, should fail validation
var isValid = item.Validate();

I found this code in a sample online:

var item = new MyExample(); // not setting any properties, should fail validation

var context = new ValidationContext(item, serviceProvider: null, items: null);
var errorResults = new List<ValidationResult>();

// carry out validation.
var isValid = Validator.TryValidateObject(item, context, errorResults);

// isValid will be FALSE

Which gives me "isValid = false" BUT it only seems to uphold the Required field and ignores the others.

The following code returns isValid = true when I expect it to return false:

var item = new MyExample() {
    RequiredFieldTest = "example text"
};

var context = new ValidationContext(item, serviceProvider: null, items: null);
var errorResults = new List<ValidationResult>();

// carry out validation.
var isValid = Validator.TryValidateObject(item, context, errorResults);

// isValid will be TRUE - not expected behavior

All other validation attempts using attributes (string length, range, max length, datatype etc) all pass as valid.

Has anyone seen this behaviour before or know why it's happening?

Rob
  • 6,819
  • 17
  • 71
  • 131

1 Answers1

8

TryValidateObject by default validates only the required attribute. You can pass it a fourth parameter validateAllProperties = true, to validate the other attributes.

if (!Validator.TryValidateObject(item, context, errorResults, true)) {
    //invalid
}
rhytonix
  • 968
  • 6
  • 14
  • This is great! Works perfectly for all *except* the [DataType] attribute. If I set DataType to be DataType.Email or something for testing, it still returns its valid - even if I just set a random data object or string that's definitely not an email. Any ideas why this one isn't being picked up? – Rob Jan 15 '20 at 16:36
  • 1
    You seem to keep stumbling on the most unintuitive aspects of the framework. `DataType` despite inheriting from `ValidationAttribute` doesn't perform validation, and is used for display purposes, it overrides `IsValid` to always return `true`. Check [DataTypeAttribute.cs](https://github.com/microsoft/referencesource/blob/master/System.ComponentModel.DataAnnotations/DataAnnotations/DataTypeAttribute.cs#L89). Instead use a custom validation, or the `EmailAddress` attribute. – rhytonix Jan 15 '20 at 17:01
  • 1
    Why does `TryValidateObject` require `item` if `context` already references it? Struggling to understand the purpose of the context. – xr280xr May 28 '20 at 16:05
  • That's an interesting question, I've always meant to look into it, so thanks for the reminder! For the first question, according to the [ValidationContext](https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs#L103) class, "during validation, especially property-level validation, the object instance might be in an indeterminate state... consume this instance with caution". Check `GetPropertyValues()` in `Validator.cs`. – rhytonix May 29 '20 at 18:38
  • For the second question, the `ValidationContext` does as the name says, it provides contextual information of the validation taking place so other classes that actually perform the validation can access this information (information like `MemberName` or `DisplayName` of the property being validated). To see an example of how the context is being used and how this information is accessed, check `ValidationAttribute.cs`'s `IsValid()` method. – rhytonix May 29 '20 at 18:42