Skip to content

v5.8.0

Choose a tag to compare

@github-actions github-actions released this 22 Jul 15:51
· 31 commits to master since this release

5.8.0 (2026-07-22)

The release that turns the providers into a modeled, enterprise-grade data layer: one store-neutral modeling vocabulary, lifecycle and concurrency semantics, value converters, live-schema evolution, and more native query surface — validated against seven real stores.

Store-neutral modeling — one vocabulary for every provider

  • eQuantic-owned annotations interpreted by all seven providers — no driver attributes on entities ([BsonElement], EF's [Column]), no rewrite when an entity moves between stores, names that never clash with System.ComponentModel.DataAnnotations: [Entity], [EntityKey], [StoredAs], [Unmapped], [PartitionKey], [ClusteringKey], [ConcurrencyToken], [Counter], [SearchIndex], [TimeToLive].
  • Explicit precedence: conventions < annotations < fluent. Each provider honours the subset that maps to its store and ignores the rest.
  • The model never lies: model.Explain(dialect) reports every mapping decision — names, stored types, keys, tokens, converters, navigations, lifecycle — the way Explain() reports a query.

Enterprise write semantics

  • Entity lifecycle by convention (eQuantic.Core.Domain / eQuantic.Core.DataModel): CreatedAt/UpdatedAt stamped on every provider (set-based updates included), and IEntityTimeEnded deletes become soft deletes with an automatic live-rows filter on reads and set-based writes.
  • DataConventions to tune it: Clock (a TimeProvider — deterministic in tests), LifecycleStamps/SoftDelete toggles, and CurrentUserId — a per-request accessor that also stamps the who (CreatedById/UpdatedById/DeletedById, the DataModel shapes) by property-name convention.
  • Optimistic concurrency: ConcurrencyToken(x => x.Version) on the relational model matches and bumps the token on every write; a commit that misses rows throws ConcurrencyConflictException and rolls back. Cosmos does the same via ConcurrencyToken(x => x.ETag) (If-Match).
  • Value converters (relational): .Converts(x => x.Email, e => e.Value, EmailAddress.Create) maps Value Objects and enums-as-strings to stored scalars at every boundary — DDL, inserts/updates, filters, materialization. The domain type never reaches the driver.

More native query surface

  • jsonb document columns (PostgreSQL): scalar-keyed dictionaries map to jsonb; ContainsKey?, the indexer → ->>.
  • SASI LIKE pushdown (Cassandra): a [SearchIndex]/SearchIndex(...) column serves StartsWith/EndsWith/Contains and Db.Like as native LIKE — the migration creates the index.
  • MariaDB dialect: AddMariaDbDatabase(...) — the MySQL engine and driver plus INSERT … RETURNING, so generated keys read back (which the MySQL dialect honestly rejects).

Richer fluent migrations

  • Live-schema evolution: AddField/DropField (ALTER TABLE ADD/DROP COLUMN; document stores gain fields on write, $unset on Mongo).
  • Rich index options: typed partial/filtered indexes (o.Filtered(x => ...), the predicate interpreted like a query filter), PostgreSQL GIN, MongoDB text and TTL — each rejected with guidance where a store can't build it.

Declared navigations & resilience

  • Reference(...)/Collection(...) override the FK conventions for legacy schemas, and dotted include paths (Include("Invoices.Order")) load level by level — one IN query per segment.
  • Transient-fault retries (relational, opt-in): AddRelationalResilience(...) — reads retry automatically, commits only behind RetryCommits, nothing retries inside an explicit transaction; transience comes from the driver (DbException.IsTransient). The document/wide-column drivers ship native retry and need none.

Grouped & aggregate reads (from 5.7, now provider-wide)

  • Typed GroupBy with HAVING on the relational providers, MongoDB ($group) and Cassandra (primary-key CQL GROUP BY); Min/Max/Average push down on all seven. On Cosmos DB, GroupBy is rejected honestly for now (the SDK's LINQ GroupBy emits SELECT VALUE {…}, invalid with GROUP BY; group client-side there) — a hand-built Cosmos SQL GROUP BY is tracked for a follow-up.

Engineering

  • The eQuantic.Core.Data.Query engine seam (interpreters + dialect-agnostic IR) is a declared provider SPI: public so third-party dialects can build on it, hidden from application IntelliSense ([EditorBrowsable(Never)]), versioned with the engine.
  • Cosmos testing moves to the vNext emulator: runs on ARM64 (Cosmos is now locally testable for the first time), gated on the /ready health probe with a data-plane (pgcosmos) bootstrap retry — the chronic 408/503 CI collapses are gone.

Validated against real servers: PostgreSQL, MySQL 8, MariaDB, SQL Server 2022, Cassandra 4.1 (SASI enabled), MongoDB replica set (Testcontainers) and the Azure Cosmos DB vNext emulator — 350+ tests.