Declarative Validation for nested collections?

to keep database updates atomic, i’m tending towards more complex DTO’s to carry the entire unit of work… it was pleasing to see service stack declarative validation “just work” on basic properties but i was a little surprised it doesn’t fire on nested DTO collections… i.e. where a parent DTO has a List<ChildDTO> property where the ChildDTO has it’s own declarative validation attributes applied… after googling quite a bit i’m also surprised nobody else has asked this before so i feel like i must be missing something obvious… i realize i could implement custom AbstractValidators for every parent with rules for the child collection properties but it would be nice to avoid all that boilerplate… i think i’m basically looking for what FluentValidation has provided as it’s “ImplicitlyValidateChildProperties = true” configuration option… is this something that can be enabled under ServiceStack?

Not something that I know of, but as your domain grows I’ve found a rule like that would likely create the need for awkward DTOs just to get away from validation rules that now would apply everywhere for the same DTO.

Could you provide some examples of the DTOs structures? Are they quite nested/deep or just ChildDTO reused in a lot of requests where the example same rules need to apply? Any more details of your use case would be helpful.

thanks for asking… pretty simple so far… sample DTOs below…
then if i have a service on LocatorUpdate, only the Locator validations fire… no validations seem to pop for the instances in List<LocatorData>.

//this gets used as a child list in parent class below
public class LocatorData
{
    public string Locator { get; set; }
    public string Name { get; set; }
    [ValidateMaximumLength(20)]
    public string Value { get; set; }
} 

public class Locator
{
    public string Site { get; set; }
    [ValidateNotEmpty]
    [ValidateMaximumLength(20)]
    public string LocatorId { get; set; }
    public List<LocatorData> Parameters { get; set; } // **** here's the example
}

public class LocatorUpdate : Locator, IReturn<Locator> { }

Thanks for the example. I’ve just added support for declarative collection validation in this commit where if your IEnumerable<T> collection has validators it will auto register the declarative collection validator for that type so it should now work as expected.

When a single child collection validator is thrown your Error ResponseStatus DTO should look something like:

{
    ErrorCode: "MaximumLength",
    Message: "The length of 'Value' must be 20 characters or fewer. You entered 32 characters.",
    Errors: [{
        FieldName: "Parameters[0].Value"
        ErrorCode: "MaximumLength",
        Message: "The length of 'Value' must be 20 characters or fewer. You entered 32 characters.",
    }]
}

Registering declarative collection validators is enabled by default and can be disabled with:

Plugins.Add(new ValidationFeature { 
    ImplicitlyValidateChildProperties = false
});

This change is available from the latest v5.10.5 that’s now available on MyGet.

2 Likes

fantastic! testing now and looking good :+1: thank you!

next question, i’m not doing this anywhere yet, but would this include singular DTO child properties as well? e.g.

public class Locator
{
    public string Site { get; set; }
    [ValidateNotEmpty]
    [ValidateMaximumLength(20)]
    public string LocatorId { get; set; }
    public LocatorData details { get; set; } //**** given the LocatorData defined previously
}

fyi, i notice a new warn in runtime output:

warn: ServiceStack.Serialization.StringMapTypeDeserializer[0]
      Property 'session_state' does not exist on type 'ServiceStack.Authenticate'

i see this is inconsequential from your previous posts

also, just had to mention, migrating project and all assemblies from core 3.1 to .net 5 running on azure app svc without any hiccup kindof amazes me… mythz, whatever you did to make that possible, thank you! =)

Yes that would also makes sense to auto wire up support for which I’ve added in this commit.

This is now available from the latest v5.10.5 that’s now available on MyGet, as you’ve got an existing v5.10.5 version installed you’ll need clear your NuGet packages cache to download the latest version, e.g:

$ nuget locals all -clear

Some other notes about the behavior when mixed with existing custom validators:

  • It will also auto register custom child validators defined using FluentValidation APIs
  • It wont register child validators for containing Type custom validators defined using FluentValidation APIs, to avoid duplicate registration of validators.

I hope that’s clear, basically if you’ve defined a custom validator for a containing Type (e.g. Request DTO) you’d need to register its child validators in its custom implementation.

Luckily we’ve not had to make any effort in order to support .NET 5, although we’re fairly insulated from runtime platform changes as all our libraries (excl gRPC) are dependent on .NET Standard 2.0 platform abstraction so we don’t need to ship different framework-specific .dll’s and having most of ServiceStack’s functionality being maintained ourselves means we’re not affected by many breaking changes between platform releases as it’s very unlikely they’ll be making any breaking changes made to the core .NET Standard 2.0 HTTP Abstractions that ServiceStack depends on.