Repository navigation
Expo Nested Models
Expo validates a non-null child whose declared type implements IExpoValidatable. Mark both the parent and child with [Expo] and make them partial to generate their validation methods.
// RegistrationStudent.cs
[Expo]
public sealed partial class RegistrationStudent
{
[IsRequired]
[StringMatchesEmail]
public string? Email { get; init; }
}// GroupRegistration.cs
[Expo]
public sealed partial class GroupRegistration
{
[IsRequired("A lead student is required.")]
public RegistrationStudent? Lead { get; init; }
[ItemsHasMaximumLength(4)]
public List<RegistrationStudent?>? Guests { get; init; }
}Calling GroupRegistration.TryValidate checks the parent's rules, then the child rules. A bad email on Lead produces /Lead/Email. A bad email on the first guest produces /Guests/0/Email.
No separate cascade attribute is needed. Keep the property's declared type specific enough for Expo to recognize the child contract; declaring it as object does not describe a validatable child.
A null child is skipped by nested validation. [IsRequired] on Lead reports the missing object at /Lead. Once the child exists, its email rules report failures at /Lead/Email.
A null guest list also skips nested validation. Add [IsRequired] if that list must be supplied. Null elements inside a child collection are skipped; requiring the collection does not require every child item. Use a custom rule to reject null entries when needed.
var group = new GroupRegistration
{
Lead = new RegistrationStudent { Email = "lead@example.edu" },
Guests = [new RegistrationStudent { Email = "invalid" }]
};
bool valid = group.TryValidate(out var errors);This returns false with an email failure at /Guests/0/Email. The parent keeps the child's failure message and prefixes its pointer with the parent path. Deeper nesting repeats that behavior.
Use an acyclic input graph. Generated validation follows child references and does not track visited objects to break reference cycles.
See Collections for outer size and item rules, and Errors and metadata for consuming nested paths.
- Home
- Getting started
-
Package guides
- Partie
- Expo
-
Mise
- Database setup
- Table mappings
- Aliases
- Relationships
- Result rows
- SQL interpolation
- Queries
- Filtering
- Joins
- Aggregation
- Paging and ordering
- Subqueries
- Set operations
- Common table expressions
- Readers
- Streaming
- Writers
- Inserts
- Updates
- Deletes
- Returned rows
- Custom SQL and procedures
- Transactions
- Resource lifetimes
- Database dialects
- Diagnostics
- Integrations
- Recipes
- Reference
- Troubleshooting
- Development