Skip to content

v5.2.0

Choose a tag to compare

@github-actions github-actions released this 05 Sep 08:48
· 4 commits to main since this release
b2aca4f

5.2.0

The features AuditOffice's review of its own workarounds asked for, in the order they pay off:
a validation for a failure that reports nothing, a read model so an application can check its
own obligations, filters that resolve property paths the way index parts already do, an amend
API, and typed filter predicates.

  • Changed: an index or constraint name longer than the provider's identifier limit is rejected at dotnet ef migrations add. PostgreSQL truncates a name past 63 bytes with a NOTICE and applies the migration cleanly, so the index exists under a name that neither the declaration nor a later constraint-violation error reports — a slice dispatching on the constraint name falls through in silence; SQL Server rejects the statement at apply time instead. Explicit and default names alike are checked, measured the way the provider measures them (bytes on PostgreSQL, characters on SQL Server), on the target model only. The names this package derives are never truncated, unlike EF Core's own default names, so a long table name plus a long column path reaches the limit quietly, and a default temporal foreign key name, built from two table names, is the first to. Give the declaration a name; a name-only change renames in place.

  • New: a read model. GetComplexIndexes() on an entity type or the model returns every declaration this package holds — property-level, entity-level, composite and expression indexes alike — as ComplexIndexDeclarations carrying the parts as property paths, IsUnique, Filter, the explicit Name and, for entity-level declarations, the provider options; FindComplexIndex(name) looks one up by explicit name. The PostgreSQL package adds GetExclusionConstraints() and FindExclusionConstraint(name) for exclusion constraints, whose element type ExclusionPartDefinition and annotation key NpgsqlExclusionAnnotations.Constraints are now public. Both work on the mutable model inside OnModelCreating, so an application can check a convention such as "every unique index and exclusion constraint on a withdrawable aggregate is filtered to live rows" while the model is built. The differ builds its own descriptors from the same readers, so what the read model reports is what the migration is scaffolded from.

  • New: filters resolve {Property.Path} placeholders, the way expression parts already did — on complex, composite and expression indexes, and on PostgreSQL exclusion constraints. filter: "{RevokedAt} IS NULL" becomes "revoked_at" IS NULL ([revoked_at] on SQL Server) at migrations add, honouring HasColumnName and, on PostgreSQL, ToJson() members; the resolved text is what the migration carries, rendered by the stock generator with no runtime wiring, and what the snapshot is compared on. Only a brace pair holding a dotted identifier path outside a single-quoted literal is a placeholder, so existing filters with array or JSON literals ('{urgent}', '{"a": 1}') are unaffected, and a placeholder naming no property fails loudly. Template resolution now lives in the core differ over a new QuoteIdentifier seam, which is also why the SQL Server satellite resolves them.

  • New: property paths see through a value converter. For a value object mapped as one column, x => x.Email.Value — in HasComplexIndex, a typed HasExpressionIndex, an exclusion element, or a filter placeholder — resolves to that column, provided the member's type is the converter's provider type. Without that guard x.CreatedAt.Year would have indexed the whole column; it still fails, and says so.

  • New: a mutable API. On an IMutableEntityType, AddComplexIndexFilter(predicate, where) ANDs a predicate onto the filter of every selected complex index — property-level and entity-level alike — so a shared convention can install a live-rows filter the way it installs the query filter, instead of every configuration repeating it; AddComplexIndex(definition) adds an entity-level declaration with the fluent API's identity and name rules. The PostgreSQL package adds AddExclusionConstraintFilter. An unfiltered declaration gets the predicate, a filtered one (existing) AND (predicate), one that already carries it is left alone, so the calls are safe to repeat. They amend what is declared at the time of the call, so they belong after the configurations.

  • New: typed filter predicates on PostgreSQL. Every filter: string has a lambda form — HasComplexIndex(x => x.Email, x => x.RevokedAt == null), HasComplexCompositeIndex(…, x => …), HasExpressionIndex(…, x => …), HasExclusionConstraint(…, x => …) — and the index and constraint builders take HasFilter(x => …) (HasFilter<TEntity> on the non-generic index builders). The predicate is translated at the declaration into a filter template, so the column names come from the model: ==/!= (IS NULL/IS NOT NULL against null), <, <=, >, >=, &&, ||, !, boolean properties, and on the operands the string operations and constants a typed expression index accepts. Enums are refused — how one is stored depends on a value conversion the filter cannot see, so Status == Status.Active would compare a text column against 0 and fail at apply time — as are values with no portable SQL spelling (DateTime, Guid), which the typed expression translator used to render as bare text. The amend calls have typed forms too.

Published to nuget.org through Trusted Publishing by this run. The CycloneDX SBOM for each package is attached below.