Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions generator/.DevConfigs/bc057d5e-a710-458b-ac2e-92e0cf88b741.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"services": [
{
"serviceName": "DynamoDBv2",
"type": "patch",
"changeLogMessages": [
"Implement DynamoDBDerivedTypeAttribute to enable polymorphism support for nested items on save and load data."
]
}
]
}
62 changes: 62 additions & 0 deletions sdk/src/Services/DynamoDBv2/Custom/DataModel/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,38 @@ public DynamoDBTableAttribute(string tableName, bool lowerCamelCaseProperties)
}
}

/// <summary>
/// DynamoDB attribute that marks a class for polymorphism support.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add more depth to the docs. Explaining how you add multiple declaration of the attribute for each possible subtype and how the TypeDiscriminator is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added extra code comments for docs

///
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]
public sealed class DynamoDBPolymorphicTypeAttribute : DynamoDBAttribute
{
/// <summary>
/// Unique name discriminator of the derived type.
/// </summary>
public string TypeDiscriminator { get; }

/// <summary>
/// Derived type of the Property type.
/// Type must be a subclass of the Property type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the property type is an interface? Instead of a subclass can it be a type that implements the interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both interfaces and and subclasses are supported. Integration tests added cover this scenario.

/// </summary>
[DynamicallyAccessedMembers(InternalConstants.DataModelModeledType)]
public Type DerivedType { get; }

/// <summary>
/// Construct an instance of DynamoDBPolymorphicTypeAttribute
/// </summary>
/// <param name="typeDiscriminator"></param>
/// <param name="derivedType"></param>
public DynamoDBPolymorphicTypeAttribute(string typeDiscriminator,
[DynamicallyAccessedMembers(InternalConstants.DataModelModeledType)] Type derivedType)
{
TypeDiscriminator = typeDiscriminator;
DerivedType = derivedType;
}

}
/// <summary>
/// DynamoDB attribute that directs the specified attribute not to
/// be included when saving or loading objects.
Expand Down Expand Up @@ -243,6 +275,36 @@ public DynamoDBPropertyAttribute(string attributeName, bool storeAsEpoch)
public bool StoreAsEpoch { get; set; }
}

/// <summary>
/// DynamoDB Polymorphic Property Attribute that marks up current member for polymorphism support.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused on the purpose of separating the attribute that goes on the type vs the property. Seems like they solve same purpose. You are giving them the option of defining the polymorphism on either the type or the property. What happens if the declaration is on both the type and the property that uses the type. Should that be allowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reworked the implementation in order to have one single attribute for this scope. If the attribute is used on both the type and property, the property one allows more granular control, overriding or supplementing the class-level behavior.

/// Specifies the field name to be used as the type discriminator and the derived types.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
public sealed class DynamoDBPolymorphicPropertyAttribute : DynamoDBPropertyAttribute
{
/// <summary>
/// Unique name discriminator of the derived type.
/// </summary>
public string TypeDiscriminator { get; }

/// <summary>
/// Derived type of the Property type.
/// Type must be a subclass of the Property type.
/// </summary>
public Type DerivedType{ get; }

/// <summary>
/// Construct an instance of DynamoDBPolymorphicPropertyAttribute
/// </summary>
/// <param name="typeDiscriminator">Name of the field to be used as the type discriminator.</param>
/// <param name="derivedType">Derived type names and their corresponding types.</param>
public DynamoDBPolymorphicPropertyAttribute(string typeDiscriminator, Type derivedType)
{
TypeDiscriminator = typeDiscriminator;
DerivedType = derivedType;
}
}

/// <summary>
/// DynamoDB property that marks up current member as a hash key element.
/// Exactly one member in a class must be marked with this attribute.
Expand Down
15 changes: 15 additions & 0 deletions sdk/src/Services/DynamoDBv2/Custom/DataModel/Configs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ public DynamoDBContextConfig()
/// Service calls made via <see cref="AmazonDynamoDBClient"/> will always return
/// <see cref="DateTime"/> attributes in UTC.</remarks>
public bool? RetrieveDateTimeInUtc { get; set; }

/// <summary>
/// Property indicating the name of the attribute used for derived types conversion.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand on this documentation. I assume this is the attribute that stores the metadata used by this library to figure out the type to instantiate.

This attribute is missing listing the default attribute.

/// </summary>
public string DerivedTypeAttributeName { get; set; }
}

/// <summary>
Expand Down Expand Up @@ -434,6 +439,9 @@ public DynamoDBFlatConfig(DynamoDBOperationConfig operationConfig, DynamoDBConte
!string.IsNullOrEmpty(operationConfig.IndexName) ? operationConfig.IndexName : DefaultIndexName;
List<ScanCondition> queryFilter = operationConfig.QueryFilter ?? new List<ScanCondition>();
ConditionalOperatorValues conditionalOperator = operationConfig.ConditionalOperator;
string derivedTypeAttributeName =
//!string.IsNullOrEmpty(operationConfig.DerivedTypeAttributeName) ? operationConfig.DerivedTypeAttributeName :
!string.IsNullOrEmpty(contextConfig.DerivedTypeAttributeName) ? contextConfig.DerivedTypeAttributeName : "$type";

ConsistentRead = consistentRead;
SkipVersionCheck = skipVersionCheck;
Expand All @@ -449,6 +457,7 @@ public DynamoDBFlatConfig(DynamoDBOperationConfig operationConfig, DynamoDBConte
MetadataCachingMode = metadataCachingMode;
DisableFetchingTableMetadata = disableFetchingTableMetadata;
RetrieveDateTimeInUtc = retrieveDateTimeInUtc;
DerivedTypeAttributeName = derivedTypeAttributeName;

State = new OperationState();
}
Expand Down Expand Up @@ -550,6 +559,12 @@ public DynamoDBFlatConfig(DynamoDBOperationConfig operationConfig, DynamoDBConte
// State of the operation using this config
internal OperationState State { get; private set; }

/// <summary>
/// Property indicating the name of the attribute used for derived types conversion.
/// Default value is "$type" if not set in the config.
/// </summary>
public string DerivedTypeAttributeName { get; set; }

public class OperationState
{
private CircularReferenceTracking referenceTracking;
Expand Down
Loading