Swashbuckle Integration
OhData's core EnGen.OhData.AspNetCore package carries no OpenAPI-generator dependency. To have
Swashbuckle document the OData query parameters ($filter, $orderby, $top, $skip, $select,
$expand, $count, $search) on OhData's collection GET endpoints — and keep the generated
schemas faithful to the real wire shape — add the EnGen.OhData.AspNetCore.Swashbuckle companion
package:
dotnet add package EnGen.OhData.AspNetCore.Swashbuckle
Registration
The recommended one-liner is c.AddOhData(). It is the canonical wiring recipe — you do not need to
know the filter class names:
using OhData.AspNetCore.Swashbuckle;
builder.Services.AddSwaggerGen(c => c.AddOhData());
This registers both the operation filter (documents the OData query parameters) and the schema
filter (schema fidelity for Ignore(...)d properties and response casing — see
below).
À la carte
Each filter is independent. To register only one, call it directly instead of AddOhData():
builder.Services.AddSwaggerGen(c =>
{
c.OperationFilter<OhDataSwaggerOperationFilter>();
c.SchemaFilter<OhDataSwaggerSchemaFilter>();
});
The one-line minimum is just the operation filter:
builder.Services.AddSwaggerGen(c => c.OperationFilter<OhDataSwaggerOperationFilter>());
Minimal API endpoints need ASP.NET Core's ApiExplorer enabled for Swashbuckle to discover them at
all:
builder.Services.AddEndpointsApiExplorer();
What gets documented
OhDataSwaggerOperationFilter implements IOperationFilter. For every endpoint that carries the
OhDataQueryOptionsMetadata OhData attaches at registration time, it adds the corresponding query
parameters, driven by the entity set's capability flags:
| Parameter | Added when |
|---|---|
$top / $skip |
Always, once per operation (whenever OhDataQueryOptionsMetadata is present and $top isn't already documented) |
$filter |
FilterEnabled |
$orderby |
OrderByEnabled |
$select |
SelectEnabled |
$expand |
ExpandEnabled |
$count |
CountEnabled |
$search |
a Search handler is configured (SearchEnabled) |
The $top parameter's description includes the entity set's MaxTop value when one is configured,
so consumers of the generated document see the server-enforced page-size cap. The $top/$skip
guard only checks whether OhDataQueryOptionsMetadata is present and $top isn't already
documented, so it is idempotent with respect to a parameter another filter added under the same
name.
OhDataQueryOptionsMetadata is attached to more than the top-level collection GET route — it is
also present on GET /{EntitySet}/$count and on the single-entity GET /{EntitySet}({key}) route
(which supports $select/$expand in its own right), so those routes pick up $top/$skip too.
This is intentional, existing behavior shared with the OpenAPI and NSwag companions.
Request bodies, typed collection responses, and read-path summaries
Write routes get a real request-body schema and collection GET routes get a typed
ODataCollectionResponse<T> envelope automatically, via OhDataApiDescriptionProvider in the core
package — no Swashbuckle-specific setup needed beyond AddSwaggerGen itself. See
openapi.md for the full description; it applies
identically here since all three doc stacks read the same ApiDescription.
WithSummary()/WithDescription() on collection GET routes (see
openapi.md) are not surfaced by Swashbuckle automatically;
OhDataSwaggerOperationFilter applies them explicitly from
IEndpointSummaryMetadata/IEndpointDescriptionMetadata endpoint metadata, without overwriting a
summary/description already populated from another source (e.g. XML doc comments).
Ignored properties and schema casing
OhDataSwaggerSchemaFilter implements ISchemaFilter and keeps generated schemas faithful to the
real wire shape in two ways, both keyed by CLR model type:
- Ignored properties omitted. Properties excluded via
EntitySetProfile.Ignore(...)never cross the wire (see ignoring-properties.md), but OpenAPI schemas are generated from the CLR type — which still has the property. The filter removes each ignored member (and itsrequiredentry) from its model type's generated schema, request and response alike, since both share the component schema. Matching is by CLR member, immune to the naming policy, so a same-named property on a different (un-ignored) type is untouched. - Schema casing matches the wire. OhData owns its response JSON casing — PascalCase by default,
independent of the host's
HttpJsonOptions(see query-options.md → JSON property casing). The filter renames each surviving schema property key from the host serializer's casing (camelCase by ASP.NET Core default) to OhData's response casing, so the document advertises exactly what responses emit. A[JsonPropertyName]rename wins over the policy, matching the response precedence.
The filter uses Swashbuckle's own ISerializerDataContractResolver to map CLR property names to the
JSON names the schema keys use, and resolves the OhData registrations lazily at
document-generation time (by which point app.MapOhData() has forced every registration to
resolve, so the maps cannot be stale).
Versioned / multi-document setup
For multiple AddOhData/MapOhData registrations, partition documents by Swagger document name
with a DocInclusionPredicate, pairing it with WithGroupName() on the route group returned by
MapOhData(). The two filters above still apply per document — they read the same endpoint
metadata regardless of which document an operation lands in. See
versioning.md for the full multi-document walkthrough
(SwaggerDoc, DocInclusionPredicate, and WithGroupName()).
Same convention as the other companions
This package is the Swashbuckle counterpart to the built-in
Microsoft.AspNetCore.OpenApi and NSwag companions. All three read the
same OhDataQueryOptionsMetadata endpoint metadata and apply the same gating rules, so switching
between the OpenAPI generation pipelines does not change what gets documented.