Modern .NET applications increasingly need to store data that doesn’t fit neatly into relational tables, whether it’s per-tenant custom fields, evolving product attributes, or external API payloads. Before EF Core 10, handling this kind of flexible data meant awkward modeling workarounds, raw JSON text columns, or scattered NoSQL side-stores.
In this blog, I’ll show you how EF Core 10 introduces a powerful new way to map JSONB columns in PostgreSQL through Complex Types. Combined with PostgreSQL’s highly optimized JSONB storage engine, this enables clean modeling of flexible, schema-evolving data, without sacrificing performance or queryability. I’ll help you understand when JSONB is the right tool, how EF Core 10 improves JSON mapping, and best practices for designing hybrid relational-document models.
Why JSONB Matters
Traditional relational models break when your application needs to store:
- Dynamic or tenant-specific fields
- Hierarchical or nested attributes
- Evolving structures that change frequently
- External API payloads or metadata
This is exactly where PostgreSQL JSONB shines: you get schema flexibility, native indexing, fast querying, and full ACID guarantees, all inside a relational engine.
JSON vs JSONB in PostgreSQL
| Feature | JSON | JSONB |
|---|---|---|
| Storage | Text | Binary tree |
| Query performance | Slow | Fast |
| Indexes | None | GIN/GiST |
| Duplicate keys | Preserved | Last wins |
| Parse cost | Per query | Once at insert |
Rule of thumb: Always use JSONB unless you need to preserve formatting, then use JSON.
EF Core 10 Transforms JSON Mapping
Before .NET 10, JSONB mapping required owned entities, causing:
- Confusing ownership semantics
- Shadow primary keys
- Verbose configuration
- No support for
ExecuteUpdate
The .NET 10 Solution: Complex Types
EF Core 10 introduces Complex Types, providing:
- Value-type semantics
- Cleaner configuration
- Automatic nested collections
- Full LINQ → JSONB translation
- Bulk JSON updates with
ExecuteUpdate - Optional complex types (
Address?)
Configuration Example:
modelBuilder.Entity<Product>()
.ComplexProperty(p => p.Specs, b => b.ToJson());
That’s it—EF Core handles nested structures automatically.
Querying JSONB in EF Core 10
EF Core now translates complex LINQ queries directly into PostgreSQL JSONB operators such as ->, ->>, @>.
Filter by JSON attribute
var items = await context.Products
.Where(p => p.Specs.Brand == "Apple")
.ToListAsync();
Filter by nested number field
var results = await context.Products
.Where(p => p.Specs.RAM >= 16)
.ToListAsync();
Query JSON arrays
var items = await context.Products
.Where(p => p.Specs.Features.Contains("Waterproof"))
.ToListAsync();
Bulk JSON Updates with ExecuteUpdate (EF Core 10)
EF Core 10 brings real bulk-update support for JSONB:
await context.Products
.ExecuteUpdateAsync(s =>
s.SetProperty(p => p.Metadata.Views,
p => p.Metadata.Views + 1));
Performance Comparison (10,000 rows)
| Approach | Time | Memory | SQL Statements |
|---|---|---|---|
| Load + SaveChanges | 5–10s | High | 10,000 |
| ExecuteUpdate | 100–200ms | Low | 1 |
This is a huge improvement for analytics counters, status updates, metadata changes, etc.
When JSONB Is the Right Choice
Use JSONB for data that is:
✔ Schema-flexible
Metadata, preferences, workflow definitions, configuration.
✔ Hierarchical
Nested objects or lists that don’t map well to relational tables.
✔ Frequently evolving
Dynamic fields that change without requiring migrations.
✔ Semi-structured or external
Webhook payloads, API responses, integrations.
✔ Snapshot-based
Audit trails, version history, revision logs.
When NOT to Use JSONB
Avoid JSONB for:
❌ Stable, core domain data
Relational columns are faster and enforce constraints.
❌ Foreign key relationships
JSONB cannot enforce referential integrity.
❌ Join-heavy workloads
JOINs on relational fields outperform JSON extraction.
❌ High-write OLTP workloads
Updating JSONB rewrites the entire document.
Indexing JSONB Correctly
Without indexing, JSONB queries degrade quickly.
GIN index (most common)
CREATE INDEX idx_specs_gin ON products USING gin (specs);
Expression index for a specific field
CREATE INDEX idx_brand ON products ((specs ->> 'Brand'));
Use GIN for containment queries and expression indexes for filtering specific keys.
Designing a Hybrid Schema (The Recommended Approach)
The strongest architectures combine relational and JSONB styles:
Relational columns for:
- Stable fields
- Frequently queried attributes
- Joins and foreign keys
JSONB for:
- Optional fields
- Dynamic or tenant-specific attributes
- Metadata, preferences, and workflows
Example model:
public class Product
{
public int Id { get; set; }
public string Category { get; set; } = null!;
public decimal Price { get; set; }
// Flexible layer
public ProductSpecifications Specs { get; set; } = new();
}
This gives you schema safety and flexibility.
Performance Summary
JSONB is Faster For:
- Containment queries (
@>) - Reading entire documents
- Avoiding a few table joins
JSONB is Slower For:
- Aggregations on large datasets
- Frequent updates to large documents
- Complex JOIN logic
- Queries needing strict relational constraints
Practical Example: EF Core Configuration
modelBuilder.Entity<Order>(entity =>
{
entity.ComplexProperty(o => o.Metadata, b => b.ToJson());
entity.ComplexCollection(o => o.Items, b => b.ToJson());
});
Query
var orders = await context.Orders
.Where(o => o.Items.Any(i => i.UnitPrice > 100))
.ToListAsync();
Bulk update
await context.Orders
.Where(o => o.Metadata.Status == "Pending")
.ExecuteUpdateAsync(s =>
s.SetProperty(p => p.Metadata.Status, "Processing"));
Conclusion
EF Core 10 finally provides a clean, powerful, and first-class way to use PostgreSQL JSONB through Complex Types.
Key Takeaways
- Complex Types are the new standard for JSON mapping in .NET 10
- JSONB is ideal for flexible, evolving, hierarchical data
ExecuteUpdateimproves performance for JSON updates- Use a hybrid relational + JSONB model for the best architecture
- JSONB is powerful—but not a replacement for relational design
When used strategically, JSONB becomes one of the most effective tools available to .NET developers building modern, flexible applications.
If your team is considering PostgreSQL JSONB, evaluating a hybrid model, or planning a modernization effort, we can help you chart the right architecture and avoid the common pitfalls. Contact Trailhead to discuss your project. We love helping teams modernize successfully!


