Query Models
Query models expose EF Core entities directly as GraphQL queries with automatic cursor pagination, filtering, sorting, and projection. Unlike [TraxQuery] which wraps a train (business logic), [TraxQueryModel] maps a database table to a GraphQL field with zero boilerplate.
Quick Start
- Mark your entity with
[TraxQueryModel]:
[TraxQueryModel(Description = "Player profiles")]
public class PlayerRecord
{
public long Id { get; set; }
public string PlayerId { get; set; } = "";
public string DisplayName { get; set; } = "";
public int Rating { get; set; }
}- Add the entity to a
DbSet<T>on aDbContext:
public class GameDbContext(DbContextOptions<GameDbContext> options) : DbContext(options)
{
public DbSet<PlayerRecord> Players { get; set; } = null!;
}- Register the DbContext and enable model discovery:
builder.Services.AddDbContextFactory<GameDbContext>(options =>
options.UseNpgsql(connectionString));
builder.Services.AddTraxGraphQL(graphql =>
graphql.AddDbContext<GameDbContext>());This generates a playerRecords query field under discover:
query {
discover {
playerRecords(first: 10, where: { rating: { gte: 1500 } }, order: { rating: DESC }) {
nodes {
playerId
displayName
rating
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
}TraxQueryModel Attribute
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class TraxQueryModelAttribute : Attribute
{
public string? Name { get; init; }
public string? Description { get; init; }
public string? DeprecationReason { get; init; }
public string? Namespace { get; init; }
public bool Paging { get; init; } = true;
public bool Filtering { get; init; } = true;
public bool Sorting { get; init; } = true;
public bool Projection { get; init; } = true;
}Properties
| Property | Type | Default | Description |
|---|---|---|---|
Name | string? | null | Overrides the auto-derived GraphQL field name. When null, derived by pluralizing and camelCasing the class name (e.g. Player → players). |
Description | string? | null | Human-readable description that appears in the GraphQL schema documentation. |
DeprecationReason | string? | null | Marks the generated field as deprecated in the schema. |
Namespace | string? | null | Groups this field under a sub-namespace. When set, the field appears under discover { namespace { field } } instead of directly under discover. |
Paging | bool | true | Enables cursor-based pagination (Relay Connection spec). When true, the field returns a Connection type with nodes, edges, pageInfo, and totalCount. |
Filtering | bool | true | Enables filtering via a where argument. HotChocolate generates filter input types for all entity properties. |
Sorting | bool | true | Enables sorting via an order argument. HotChocolate generates sort input types for all entity properties. |
Projection | bool | true | Enables field projection — only the columns requested by the GraphQL client are selected from the database. |
Feature Configuration
Each feature can be independently disabled per model. All default to true.
// Full-featured (default)
[TraxQueryModel]
public class Player { ... }
// Pagination and filtering only — no sorting or projection
[TraxQueryModel(Sorting = false, Projection = false)]
public class AuditLog { ... }
// Simple list query — no middleware at all
[TraxQueryModel(Paging = false, Filtering = false, Sorting = false, Projection = false)]
public class StatusCode { ... }When Paging = false, the field returns a plain list ([Entity!]!) instead of a Connection type.
Name Derivation
When Name is null, the field name is derived automatically:
- Pluralize the class name (naive English rules:
Player→Players,Match→Matches,Category→Categories) - camelCase the result (
Players→players)
Override with Name for cases where the automatic pluralization is incorrect:
[TraxQueryModel(Name = "people")]
public class Person { ... }AddDbContext
Register one or more DbContext types whose DbSet<T> properties contain attributed entities:
builder.Services.AddTraxGraphQL(graphql => graphql
.AddDbContext<GameDbContext>()
.AddDbContext<InventoryDbContext>());Only DbSet<T> properties where T has [TraxQueryModel] are exposed. Other DbSet properties on the same DbContext are ignored.
The DbContext must be registered in DI separately (via AddDbContext, AddDbContextFactory, or AddPooledDbContextFactory).
vs TraxQuery
[TraxQuery] | [TraxQueryModel] | |
|---|---|---|
| Target | Train class (workflow) | Entity class (data model) |
| Resolves via | ITrainBus.RunAsync | DbContext.Set<T>() → IQueryable |
| Input | Typed input DTO | Filter/sort/page arguments (auto-generated) |
| Output | Typed output DTO | Entity properties (with projection) |
| Use case | Business logic, computed results | Direct CRUD reads, admin dashboards |
| Schema location | discover { trainName(input: ...) } | discover { modelNames(first: ..., where: ...) } |
Both appear under the discover namespace in the GraphQL schema.