Ignoring Properties
Ignore(...) excludes model properties from the entire OData surface without touching the CLR
type — no [JsonIgnore], no DTO split. The profile, not the POCO, defines what is exposed.
public class ProductProfile : EntitySetProfile<int, Product>
{
public ProductProfile() : base(x => x.Id)
{
Ignore(x => x.CostBasis, x => x.InternalNotes);
GetById = ...;
}
}
What "ignored" means
Handlers and the data layer still see the complete CLR model. The OData surface hides the property everywhere:
| Surface | Behavior |
|---|---|
$metadata (CSDL) |
Property omitted |
$select / $filter / $orderby / $expand |
400 — same error as any unknown property |
Property routes (GET/PUT/PATCH/DELETE /Set({key})/{Prop}, /$value) |
Not registered → 404 |
Response bodies (collection, single, navigation, $expand-nested) |
Member omitted |
| POST / PUT request bodies | Member not bound — silently skipped like an unknown member |
| PATCH request bodies | Member not in the Delta<TModel> |
An $expand-nested child hides its own profile's ignored properties automatically.
Rules
- Expression selectors only (
x => x.Prop) — the member must exist on the model, so typos are compile errors. Multiple calls accumulate; duplicates are harmless. - The key property cannot be ignored (
ArgumentExceptionat theIgnorecall). - A navigation property cannot be ignored. Declaring the same property in
Ignore(...)andHasMany/HasOptional/HasRequired(either order) throwsInvalidOperationExceptionat startup. - Entity sets sharing a CLR model type must declare identical ignore sets. Suppression is
keyed by CLR type across a registration, so
app.MapOhData()throws at startup if two profiles over the same type disagree. Separate registrations (AddOhData("v1", ...)/AddOhData("v2", ...)) are independent — v2 may expose a property v1 ignores. AdvancedConfigureejects the automatic EDM removal like all automatic EDM config — callconfiguration.EntityType.Ignore(...)yourself. Route suppression, wire suppression, and the validations above still apply.- ETags: an ignored property MAY participate in
UseETag(...)— useful for row-version columns that should never be exposed. - Navigation-only types (a related type with no profile of its own) have no
Ignoresurface; give the type a profile if its wire shape needs trimming.
Performance
Wire suppression uses a JsonTypeInfoResolver modifier baked into one derived
JsonSerializerOptions per registration. The modifier runs once per type (cached), so steady
state serializes fewer members than an un-ignored model — measured at 0.82× baseline time and
0.81× allocations for a 100-entity page (#226 has
the full A/B table). When no profile ignores anything, the pipeline is byte-identical to before.
OpenAPI / Swagger documents
As of #228 the companion packages omit ignored properties from generated schemas, so documents match the real wire shape. Each doc stack has a schema-level hook to register alongside its operation-level one:
- Microsoft.AspNetCore.OpenApi:
o.AddSchemaTransformer<OhDataOpenApiSchemaTransformer>()— see openapi.md - NSwag:
s.SchemaSettings.SchemaProcessors.Add(new OhDataNSwagSchemaProcessor(sp))— see nswag.md - Swashbuckle:
c.SchemaFilter<OhDataSwaggerSchemaFilter>()— see swashbuckle.md
One caveat: an OpenAPI document holds a single component schema per CLR type, so if separate registrations expose the same model type with different ignore sets (legal — see Rules above), the schemas omit the union of the sets, preferring to under-document a property one registration exposes over listing a name another registration deliberately hides.
Those same schema hooks also rename each surviving property key to OhData's response casing (PascalCase by default; see query-options.md → JSON property casing), so the documented casing matches the wire.