Skip to main content

Complex Type Support

This library resolves EF Core complex type member references in every column-referencing configuration API. A fluent selector may traverse complex-type properties (x => x.Param1.Value), and string-based configuration (data annotations, raw column lists) may use the equivalent dot-separated path ("Param1.Value") or the mapped database column name directly.

Resolution honours all registered naming conventions: a complex property Value on complex member Param1 maps to Param1_Value by default and to param1_value under EFCore.NamingConventions snake_case, for example.


Supported APIs

Complex-type member chains resolve in all of the following:

FeatureAPI
Hypertable time columnIsHypertable(x => x.Meta.Timestamp), [Hypertable("Meta.Timestamp")]
Additional dimensionsHasRangeDimension(x => x.Meta.Region, ...), HasHashDimension(...)
Chunk-skip columnsWithChunkSkipping(x => x.Meta.DeviceId)
Compression segment-byWithCompressionSegmentBy(x => x.Meta.TenantId)
Compression order-bys => [s.ByDescending(x => x.Meta.Timestamp)]
Sparse indexess => s.Bloom(x => x.Meta.DeviceId), s => s.MinMax(...)
Continuous aggregate time bucketIsContinuousAggregate<TAgg, TSource>(..., x => x.Meta.Timestamp, ...)
Aggregate functionsAddAggregateFunction(a => a.Avg, d => d.Param1.Value, EAggregateFunction.Avg)
Group-by columnsAddGroupByColumn(x => x.Param1.Name)

Nested complex types (x => x.Outer.Inner.Value) resolve recursively.

[ComplexType]
public class SensorChannel
{
public string Name { get; set; } = string.Empty;
public double Value { get; set; }
}

public class Reading
{
public Guid Id { get; set; }
public DateTime RecordedAt { get; set; }
public SensorChannel Primary { get; set; } = new();
public SensorChannel Secondary { get; set; } = new();
}
builder.IsContinuousAggregate<HourlyAggregate, Reading>(x => x.RecordedAt, "1 hour")
.AddAggregateFunction(a => a.AvgPrimary, d => d.Primary.Value, EAggregateFunction.Avg)
.AddAggregateFunction(a => a.AvgSecondary, d => d.Secondary.Value, EAggregateFunction.Avg)
.AddGroupByColumn(d => d.Primary.Name);

The time column of a hypertable or continuous aggregate may live inside a complex type; the store-type validation at model finalization traverses the path the same way and throws for invalid store types exactly as for top-level properties.


Limitations

  • JSON-mapped complex types (ComplexProperty(...).ToJson()): properties inside a JSON-mapped complex type do not have individual table columns. References to them do not resolve and the configuration entry is skipped.
  • Complex type collections (EF Core 10): collections have no per-element columns; paths through a collection complex property do not resolve.
  • Owned entity types are not traversed. Complex-type support covers [ComplexType] / ComplexProperty(...) mappings only; a path through an owned navigation does not resolve.
  • Scaffolding produces flat entities: dotnet ef dbcontext scaffold never generates [ComplexType] declarations, so a scaffolded model represents complex-type columns as ordinary flat properties. Round-tripping a complex-type model through scaffolding yields an equivalent flat model with no phantom migration diffs, because annotation values store resolved database column names that the resolver recognises in column-name form.