Table of Contents

Explain — the model never lies

Every model has an Explain() that reports every mapping decision — what convention decided, what annotations and fluent configuration declared, and what the store will actually be told. It is the same contract queries have: read the report instead of guessing.

Relational

var report = model.Explain(dialect);
SaleOrder -> table "sale_orders"
  key: (OrderId "order_id", LineNo "line_no") composite, client-generated
  concurrency token: Version "version"
  column: Name "client_name" (varchar(200))
  column: Email "email" (text) (converted from EmailAddress)
  clustering: placed_at DESC (multi-column index)
  search index: Description "description" (GIN trigram via pg_trgm; LIKE stops scanning)
  lifecycle: soft delete + live-rows filter (IEntityTimeEnded)

Every line answers a question you would otherwise answer by reading DDL: what did the column end up being called, what type did the facet produce, is the token active, did the dialect materialize the search index or ignore it (no index structure on this dialect; LIKE still pushes down, unindexed).

Cosmos DB

var report = cosmosModel.Explain();
Order -> container "orders"
  partition key: (/tenant, /region) hierarchical (multi-hash)
  clustering: /kind ASC, /at DESC (composite index on the container's policy)
  id: Code ([EntityKey])
  default TTL: 2592000s (container-level; documents expire unless they override it)
  concurrency token: _etag (writes replace conditionally with If-Match)

Cassandra

var report = cassandraModel.Explain();
Reading -> table "readings"
  partition key: (tenant_id, sensor_id)
  clustering keys: at DESC
  key column: "sensor_id" (point lookups)
  concurrency token: "version" (writes are LWTs: IF version = old, Paxos per write)
  default TTL: 604800s (table default_time_to_live; writes can override)
  column: Value "reading_value" (double)
  search index: "quality" (SASI Contains; LIKE pushes down)

MongoDB

var report = mongoModel.Explain();   // reads the driver's actual class maps — not a parallel copy
Product -> collection "products"
  id: Code "_id"
  member: Label "l"
  converts: Grade (stored as String)
  clustering: Category ASC, Score DESC (compound index)
  TTL: documents expire 3600s after CreatedAt (per-document TTL index)

Why this matters

Mapping bugs are the silent kind: a renamed column that queries miss, a token that never activated, an index you believe exists. Explain() turns each into one line you can read in a test:

Assert.That(model.Explain(dialect), Does.Contain("concurrency token: Version"));

Pin the lines you rely on. When a refactor changes the mapping, the test fails before production does. The query-side counterpart — what a read will do — is IExplainableRepository.Explain(options).