Skip to content

6.0.0

Choose a tag to compare

@LieselThuriot LieselThuriot released this 12 Jun 11:28
· 7 commits to main since this release

The headline feature of v6 is Fluent Entity support: a new way to store multiple entity types polymorphically in a single Azure Table Storage table. The release also brings automatic partition key query optimization, a smarter CreateIfNotExistsMode, and improved change-tracking defaults.

New packages

Package Description
TableStorage.Fluent Polymorphic fluent entities, 2 to 4 types per table
TableStorage.Fluent.Extended Same, for 5 to 16 types per table
TableStorage.Fluent.RuntimeCompilations Runtime LINQ compilation for fluent queries

What's new

Fluent entities

Store multiple entity types in a single table using a type discriminator. Define your models as usual and compose them:

[TableContext]
public partial class MyTableContext
{
    public TableSet<FluentTableEntity<Customer, Order>> MixedEntities { get; set; }
}

Three discriminator strategies are supported:

  • FluentTableEntity<T1, T2>: stores a $type property
  • FluentPartitionTableEntity<T1, T2>: uses PartitionKey as discriminator
  • FluentRowTypeTableEntity<T1, T2>: uses RowKey as discriminator

Fluent entities support implicit conversions in both directions, GetBackingType(), GetActualType(), GetValue() / GetValueOrDefault(), and pattern matching via SwitchCase / SwitchCaseOrDefault.

Source-generated named extension methods for fluent types

The source generator now emits named Where* and FindAsync* methods derived from the actual type names on each fluent table set. A TableSet<FluentTableEntity<Customer, Order>> property gets WhereCustomer(...) and WhereOrder(...) generated automatically, with no need to call WhereFirstType / WhereSecondType by hand.

Partition key query optimization

A new OptimizeQueries option on TableOptions (enabled by default) automatically splits OR conditions that span multiple partition keys into parallel per-partition queries, instead of a full-table scan:

// Executed as two parallel partition-scoped queries
await foreach (var entity in context.MyTable.Where(x => x.PartitionKey == "a" || x.PartitionKey == "b")) { }

Opt out via options.OptimizeQueries = false.

CreateIfNotExistsMode

The old boolean CreateTableIfNotExists / CreateContainerIfNotExists options are replaced by an enum with three values:

Value Behavior
Disabled Assumes the resource already exists; never attempts creation
Once Creates once per process lifetime; result is cached in memory
Always Always attempts creation on use (previous behavior)

Once is useful for dynamic table/container scenarios where you want to avoid repeated round-trips after the first successful creation.

Improvements

  • Change tracking defaults to Merge: when ChangesOnly = true, update and bulk operations now automatically use Merge instead of Replace, preventing unchanged properties from being overwritten with default values.
  • Fluent merge support: FluentMergeVisitorAndValidator correctly handles merging for fluent types when ChangesOnly is enabled.
  • Reduced allocations: internal refactoring of FluentTableEntity reduces heap allocations in high-throughput scenarios.
  • internal TableSets + fluent types: internal TableSet properties can now be source-generated in combination with fluent entity types.
  • Namespace clash prevention: the source generator no longer emits code into namespaces that could conflict with user-defined types.

Full changelog

5.5.0...6.0.0