> For the complete documentation index, see [llms.txt](https://brightercommand.gitbook.io/paramore-brighter-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://brightercommand.gitbook.io/paramore-brighter-documentation/darker/queriesandqueryobjects/queryobjectvalidation.md).

# Query Object Validation

Query objects should validate their parameters to ensure they receive valid data.

> **How-to** · Applies to **Darker V4** · Prerequisites: [Queries and Query Objects](/paramore-brighter-documentation/darker/queriesandqueryobjects.md)

Query objects should validate their parameters to ensure they receive valid data. Simple validation belongs in the constructor, while complex validation should be handled by the handler or a validation framework.

## Query Object Constructor Validation

Use guard clauses in the constructor for simple validation:

```csharp
using System;
using Paramore.Darker;

public sealed class GetOrdersPageQuery : IQuery<PagedResult<Order>>
{
    public GetOrdersPageQuery(int pageNumber, int pageSize)
    {
        if (pageNumber < 1)
            throw new ArgumentOutOfRangeException(
                nameof(pageNumber),
                pageNumber,
                "Page number must be positive");

        if (pageSize < 1 || pageSize > 100)
            throw new ArgumentOutOfRangeException(
                nameof(pageSize),
                pageSize,
                "Page size must be between 1 and 100");

        PageNumber = pageNumber;
        PageSize = pageSize;
    }

    public int PageNumber { get; }
    public int PageSize { get; }
}
```

## Query Object Validation Attributes

For ASP.NET scenarios, you can use data annotations that are validated by the framework:

```csharp
using System.ComponentModel.DataAnnotations;
using Paramore.Darker;

public sealed class SearchProductsQuery : IQuery<IReadOnlyList<Product>>
{
    [Required]
    [StringLength(100, MinimumLength = 2)]
    public string SearchTerm { get; init; } = string.Empty;

    [Range(1, 1000)]
    public int MaxResults { get; init; } = 50;
}
```

The ASP.NET model binder will validate these attributes before the query reaches your handler.

## Where to Validate a Query Object

**Constructor validation (recommended for queries):**

* Parameter null checks
* Range validation for numeric values
* Format validation for strings
* Basic business invariants

**Handler validation (for complex rules):**

* Database existence checks
* Authorization checks
* Complex business rules
* Cross-field validation

**Framework validation (ASP.NET):**

* Model binding validation
* Data annotations
* Request validation

```csharp
// ...
// Simple validation in constructor
public sealed class GetUserQuery : IQuery<User>
{
    public GetUserQuery(string email)
    {
        Email = !string.IsNullOrWhiteSpace(email)
            ? email
            : throw new ArgumentException("Email cannot be empty", nameof(email));
    }

    public string Email { get; }
}

// Complex validation in handler
public class GetUserQueryHandler : QueryHandlerAsync<GetUserQuery, User>
{
    private readonly IUserRepository _repository;

    public GetUserQueryHandler(IUserRepository repository)
    {
        _repository = repository;
    }

    public override async Task<User> ExecuteAsync(
        GetUserQuery query,
        CancellationToken cancellationToken = default)
    {
        // Check if user exists (complex validation)
        var user = await _repository.FindByEmailAsync(query.Email, cancellationToken);

        if (user == null)
            throw new UserNotFoundException($"User with email {query.Email} not found");

        return user;
    }
}
```

## Further Reading

* [Queries and Query Objects](/paramore-brighter-documentation/darker/queriesandqueryobjects.md) - The query objects being validated
* [Query Result Types](/paramore-brighter-documentation/darker/queriesandqueryobjects/queryresulttypes.md) - What a validated query returns
* [Implementing a Query Handler](/paramore-brighter-documentation/darker/implementaqueryhandler.md) - Where an invalid query would otherwise arrive


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://brightercommand.gitbook.io/paramore-brighter-documentation/darker/queriesandqueryobjects/queryobjectvalidation.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
