Data

Storing and Querying a Hierarchy of Types with Marten

Jeremy D. Miller shows how Marten stores a class hierarchy in one PostgreSQL table and queries it with LINQ, without the table-per-type or table-per-concrete-type decisions EF Core requires. He also covers subtype-specific partial indexes and duplicated fields. For teams weighing document storage against EF Core inheritance mapping, it is a useful comparison.

Published
Reading time
10 min read
agent run #6
agent run #6
Go to original jeremydmiller.com

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • Register subtypes with AddSubClassHierarchy() or an explicit list; the explicit list is safer for large assemblies.
  • The whole hierarchy lives in one table, with an mt_doc_type discriminator column and an automatic btree index on it.
  • Base type queries return concrete subtypes, and LoadAsync<CryptoPayment>(id) returns null for another subtype's document.
  • Discriminator values come from class names, so pin renamed subtypes with MappedType to avoid orphaned rows.
  • Duplicate<TSub>() creates a real column for a subtype field, and a Predicate on an index makes a partial index for one subtype.
  • Subtype-only properties need no migration, because new properties simply appear in the stored JSON.

Jeremy D. Miller explains how Marten stores and queries class hierarchies. EF Core asks you to pick a table-mapping strategy up front, and each choice changes the schema and generated SQL. Marten avoids that decision because documents live as JSONB in PostgreSQL, so the hierarchy fits into a single table.

The base type and its subclasses share one table with a discriminator column that Marten derives from the type name, so each row stores only its own JSON. Querying the base type returns concrete subtypes, while subtype queries add the discriminator filter automatically. Partial indexes can target a single subtype, and Duplicate<TSub>() promotes a subtype property into a real column. Shared-table costs such as autovacuum and bloat still apply.

Real-world use

  • A payments service where card, bank transfer and crypto payments share one API and one PostgreSQL table.
  • A reporting screen that filters all payments by amount and date, then narrows to one subtype with a partial index.
  • A team using Marten projections that wants inheritance-style document models without EF Core mapping decisions.

Why it matters

Because Marten needs no TPH, TPT or TPC decision, schema upkeep and subtype query tuning take far less effort. Still, one hot table shares autovacuum and bloat costs across all subtypes, so weigh write rates before adopting it.

This summary was generated by the agent from the original post on Jeremy D. Miller (The Shade Tree Developer). The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message