Summary
The immediate performance target is the runtime project, not the generator.
src/LayeredCraft.DynamoMapper.Runtime/AttributeValueExtensions/CollectionAttributeValueExtensions.cs currently does a large amount of boxing/unboxing in its generic list/map/set helper paths, especially when converting collection elements for scalar and enum types.
Scope
This issue is specifically about runtime mapping performance in LayeredCraft.DynamoMapper.Runtime.
It is not about MapperSyntaxProvider, incremental generator analysis, or attribute option parsing in the generator project.
Current Behavior
The collection helpers route generic element conversion through these methods in CollectionAttributeValueExtensions.cs:
ConvertFromAttributeValue<T>(AttributeValue av, string? format = null)
ConvertToAttributeValue<T>(T? value, string? format = null)
ParseNumber<T>(string s) where T : struct
FormatNumber<T>(T value) where T : struct
Those helpers are used by the public runtime APIs for:
TryGetList<T>() / GetList<T>() / SetList<T>()
TryGetMap<T>() / GetMap<T>() / SetMap<T>()
TryGetNumberSet<T>() / GetNumberSet<T>() / SetNumberSet<T>()
The implementation currently relies on patterns such as:
(T)(object)...
((int)(object)value)
((Guid)(object)value)
((Enum)(object)value)
That means common runtime collection paths box and unbox repeatedly for primitives, nullable primitives, date/time values, GUIDs, and enums.
Evidence in Code
In CollectionAttributeValueExtensions.cs the boxing-heavy section is concentrated in the private helper block near the bottom of the file.
Examples include:
ConvertFromAttributeValue<T>() returning values via (T)(object) for bool, numeric types, DateTime, DateTimeOffset, TimeSpan, DateOnly, TimeOnly, Guid, byte[], MemoryStream, and Stream
ConvertToAttributeValue<T>() casting incoming values back out via (object) before formatting/serializing them
ParseNumber<T>() returning parsed numeric values via (T)(object)
- enum serialization going through
((Enum)(object)value).ToString(format)
Important Context
The runtime project already has type-specific helper surfaces in separate files:
NumericAttributeValueExtensions.cs
EnumAttributeValueExtensions.cs
DateTimeAttributeValueExtensions.cs
DateOnlyTimeOnlyAttributeValueExtensions.cs
GuidAttributeValueExtensions.cs
BooleanAttributeValueExtensions.cs
StringAttributeValueExtensions.cs
BinaryAttributeValueExtensions.cs
So the main problem is not lack of type-specific logic. It is that the generic collection helper layer currently collapses many supported element types back into object-based conversion.
Proposed Fix
Refactor the runtime collection conversion path so supported element types do not need to round-trip through object in the hot path.
Possible directions:
- Split common supported element types into strongly typed branches or specialized generic fast paths.
- Reuse existing runtime type-specific helpers where practical instead of re-parsing/re-formatting through
object casts.
- Keep unsupported types failing with the current
NotSupportedException behavior.
- Preserve existing null-handling, format-handling, and Dynamo kind behavior.
Acceptance Criteria
- The issue is resolved in the runtime project, not the generator project.
CollectionAttributeValueExtensions.cs no longer relies on repeated (object) boxing/unboxing in the main generic collection conversion path for supported scalar/enum element types.
- Behavior stays the same for null collection elements, nullable element types, format-sensitive types, and supported set/list/map operations.
- Tests cover the affected runtime paths, especially primitives, enums, GUID/date-time types, and nullable collection elements.
- Some form of allocation/perf validation is included so improvements can be verified and regressions are easier to catch later.
Summary
The immediate performance target is the runtime project, not the generator.
src/LayeredCraft.DynamoMapper.Runtime/AttributeValueExtensions/CollectionAttributeValueExtensions.cscurrently does a large amount of boxing/unboxing in its generic list/map/set helper paths, especially when converting collection elements for scalar and enum types.Scope
This issue is specifically about runtime mapping performance in
LayeredCraft.DynamoMapper.Runtime.It is not about
MapperSyntaxProvider, incremental generator analysis, or attribute option parsing in the generator project.Current Behavior
The collection helpers route generic element conversion through these methods in
CollectionAttributeValueExtensions.cs:ConvertFromAttributeValue<T>(AttributeValue av, string? format = null)ConvertToAttributeValue<T>(T? value, string? format = null)ParseNumber<T>(string s) where T : structFormatNumber<T>(T value) where T : structThose helpers are used by the public runtime APIs for:
TryGetList<T>()/GetList<T>()/SetList<T>()TryGetMap<T>()/GetMap<T>()/SetMap<T>()TryGetNumberSet<T>()/GetNumberSet<T>()/SetNumberSet<T>()The implementation currently relies on patterns such as:
(T)(object)...((int)(object)value)((Guid)(object)value)((Enum)(object)value)That means common runtime collection paths box and unbox repeatedly for primitives, nullable primitives, date/time values, GUIDs, and enums.
Evidence in Code
In
CollectionAttributeValueExtensions.csthe boxing-heavy section is concentrated in the private helper block near the bottom of the file.Examples include:
ConvertFromAttributeValue<T>()returning values via(T)(object)forbool, numeric types,DateTime,DateTimeOffset,TimeSpan,DateOnly,TimeOnly,Guid,byte[],MemoryStream, andStreamConvertToAttributeValue<T>()casting incoming values back out via(object)before formatting/serializing themParseNumber<T>()returning parsed numeric values via(T)(object)((Enum)(object)value).ToString(format)Important Context
The runtime project already has type-specific helper surfaces in separate files:
NumericAttributeValueExtensions.csEnumAttributeValueExtensions.csDateTimeAttributeValueExtensions.csDateOnlyTimeOnlyAttributeValueExtensions.csGuidAttributeValueExtensions.csBooleanAttributeValueExtensions.csStringAttributeValueExtensions.csBinaryAttributeValueExtensions.csSo the main problem is not lack of type-specific logic. It is that the generic collection helper layer currently collapses many supported element types back into
object-based conversion.Proposed Fix
Refactor the runtime collection conversion path so supported element types do not need to round-trip through
objectin the hot path.Possible directions:
objectcasts.NotSupportedExceptionbehavior.Acceptance Criteria
CollectionAttributeValueExtensions.csno longer relies on repeated(object)boxing/unboxing in the main generic collection conversion path for supported scalar/enum element types.