diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Attributes.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Attributes.cs
index 77c8231db416..fe667b49c761 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Attributes.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Attributes.cs
@@ -240,6 +240,36 @@ public DynamoDBPropertyAttribute(string attributeName, bool storeAsEpoch)
public bool StoreAsEpoch { get; set; }
}
+ ///
+ /// DynamoDB attribute that marks a class for polymorphism support.
+ /// Specifies the field name to be used as the type discriminator and the derived types.
+ ///
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
+ public sealed class DynamoDBDerivedTypeAttribute : DynamoDBPropertyAttribute
+ {
+ ///
+ /// Unique name discriminator of the derived type.
+ ///
+ public string TypeDiscriminator { get; }
+
+ ///
+ /// Derived type of the Property type.
+ /// Type must be a subclass of the Property type.
+ ///
+ public Type DerivedType{ get; }
+
+ ///
+ /// Construct an instance of DynamoDBPolymorphicAttribute
+ ///
+ /// Name of the field to be used as the type discriminator.
+ /// Derived type names and their corresponding types.
+ public DynamoDBDerivedTypeAttribute(string typeDiscriminator, Type derivedType)
+ {
+ TypeDiscriminator = typeDiscriminator;
+ DerivedType = derivedType;
+ }
+ }
+
///
/// DynamoDB property that marks up current member as a hash key element.
/// Exactly one member in a class must be marked with this attribute.
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Configs.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Configs.cs
index 9eb5dad92d64..58229dd5d1eb 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Configs.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Configs.cs
@@ -140,6 +140,11 @@ public DynamoDBContextConfig()
///
/// This setting is only applicable to the high-level library. Service calls made via will always return attributes in UTC.
public bool? RetrieveDateTimeInUtc { get; set; }
+
+ ///
+ /// Property indicating the name of the attribute used for derived types conversion.
+ ///
+ public string DerivedTypeAttributeName { get; set; }
}
///
@@ -365,6 +370,9 @@ public DynamoDBFlatConfig(DynamoDBOperationConfig operationConfig, DynamoDBConte
ConditionalOperatorValues conditionalOperator = operationConfig.ConditionalOperator;
DynamoDBEntryConversion conversion = operationConfig.Conversion ?? contextConfig.Conversion ?? DynamoDBEntryConversion.CurrentConversion;
MetadataCachingMode metadataCachingMode = operationConfig.MetadataCachingMode ?? contextConfig.MetadataCachingMode ?? DynamoDBv2.MetadataCachingMode.Default;
+ string derivedTypeAttributeName =
+ !string.IsNullOrEmpty(operationConfig.DerivedTypeAttributeName) ? operationConfig.DerivedTypeAttributeName :
+ !string.IsNullOrEmpty(contextConfig.DerivedTypeAttributeName) ? contextConfig.DerivedTypeAttributeName : "$type";
ConsistentRead = consistentRead;
SkipVersionCheck = skipVersionCheck;
@@ -380,6 +388,7 @@ public DynamoDBFlatConfig(DynamoDBOperationConfig operationConfig, DynamoDBConte
MetadataCachingMode = metadataCachingMode;
DisableFetchingTableMetadata = disableFetchingTableMetadata;
RetrieveDateTimeInUtc = retrieveDateTimeInUtc;
+ DerivedTypeAttributeName = derivedTypeAttributeName;
State = new OperationState();
}
@@ -481,6 +490,12 @@ public DynamoDBFlatConfig(DynamoDBOperationConfig operationConfig, DynamoDBConte
// State of the operation using this config
internal OperationState State { get; private set; }
+ ///
+ /// Property indicating the name of the attribute used for derived types conversion.
+ /// Default value is "$type" if not set in the config.
+ ///
+ public string DerivedTypeAttributeName { get; set; }
+
public class OperationState
{
private CircularReferenceTracking referenceTracking;
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/ContextInternal.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/ContextInternal.cs
index c6d94e93d6e7..a45913ea5d7f 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/ContextInternal.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/ContextInternal.cs
@@ -404,7 +404,7 @@ private ItemStorage ObjectToItemStorage(T toStore, bool keysOnly, DynamoDBFla
private ItemStorage ObjectToItemStorage(object toStore, Type objectType, bool keysOnly, DynamoDBFlatConfig flatConfig)
{
ItemStorageConfig config = StorageConfigCache.GetConfig(objectType, flatConfig);
- ItemStorage storage = ObjectToItemStorageHelper(toStore, config, flatConfig, keysOnly, flatConfig.IgnoreNullValues.Value);
+ ItemStorage storage = ObjectToItemStorageHelper(toStore, config, flatConfig, keysOnly, flatConfig.IgnoreNullValues != null && flatConfig.IgnoreNullValues.Value);
return storage;
}
internal ItemStorage ObjectToItemStorageHelper(object toStore, ItemStorageConfig config, DynamoDBFlatConfig flatConfig, bool keysOnly, bool ignoreNullValues)
@@ -489,15 +489,23 @@ private object FromDynamoDBEntry(SimplePropertyStorage propertyStorage, DynamoDB
Document document = entry as Document;
if (document != null)
{
- if (TryFromMap(targetType, document, flatConfig, out output))
+ if (TryFromMap(targetType, document, flatConfig, propertyStorage.DerivedTypeKeysDictionary, out output))
return output;
+ var typeAttributeName = flatConfig.DerivedTypeAttributeName;//"$type";
+ var derivedType = document.ContainsKey(typeAttributeName) ? document[typeAttributeName].AsString() : null;
+
+ if (derivedType != null && propertyStorage.DerivedTypeKeysDictionary.TryGetValue(derivedType, out var value))
+ {
+ targetType = value;
+ }
+
return DeserializeFromDocument(document, targetType, flatConfig);
}
DynamoDBList list = entry as DynamoDBList;
if (list != null &&
- TryFromList(targetType, list, flatConfig, out output))
+ TryFromList(targetType, list, flatConfig, propertyStorage.DerivedTypeKeysDictionary, out output))
{
return output;
}
@@ -507,14 +515,16 @@ private object FromDynamoDBEntry(SimplePropertyStorage propertyStorage, DynamoDB
entry, entry.GetType().FullName, propertyStorage.PropertyName, propertyStorage.MemberType.FullName));
}
}
- private bool TryFromList(Type targetType, DynamoDBList list, DynamoDBFlatConfig flatConfig, out object output)
+ private bool TryFromList(Type targetType, DynamoDBList list, DynamoDBFlatConfig flatConfig,
+ Dictionary derivedTypeKeysDictionary, out object output)
{
return targetType.IsArray ?
- TryFromListToArray(targetType, list, flatConfig, out output) : //targetType is Array
- TryFromListToIList(targetType, list, flatConfig, out output) ; //targetType is IList or has Add method.
+ TryFromListToArray(targetType, list, flatConfig, derivedTypeKeysDictionary, out output) : //targetType is Array
+ TryFromListToIList(targetType, list, flatConfig, derivedTypeKeysDictionary, out output) ; //targetType is IList or has Add method.
}
- private bool TryFromListToIList(Type targetType, DynamoDBList list, DynamoDBFlatConfig flatConfig, out object output)
+ private bool TryFromListToIList(Type targetType, DynamoDBList list, DynamoDBFlatConfig flatConfig,
+ Dictionary derivedTypeKeysDictionary, out object output)
{
if ((!Utils.ImplementsInterface(targetType, typeof(ICollection<>)) &&
!Utils.ImplementsInterface(targetType, typeof(IList))) ||
@@ -528,7 +538,7 @@ private bool TryFromListToIList(Type targetType, DynamoDBList list, DynamoDBFlat
var collection = Utils.Instantiate(targetType);
IList ilist = collection as IList;
bool useIListInterface = ilist != null;
- var propertyStorage = new SimplePropertyStorage(elementType);
+ var propertyStorage = new SimplePropertyStorage(elementType, derivedTypeKeysDictionary);
MethodInfo collectionAdd = null;
if (!useIListInterface)
@@ -550,7 +560,8 @@ private bool TryFromListToIList(Type targetType, DynamoDBList list, DynamoDBFlat
return true;
}
- private bool TryFromListToArray(Type targetType, DynamoDBList list, DynamoDBFlatConfig flatConfig, out object output)
+ private bool TryFromListToArray(Type targetType, DynamoDBList list, DynamoDBFlatConfig flatConfig,
+ Dictionary derivedTypeKeysDictionary, out object output)
{
if (!Utils.CanInstantiateArray(targetType))
{
@@ -560,8 +571,7 @@ private bool TryFromListToArray(Type targetType, DynamoDBList list, DynamoDBFlat
var elementType = Utils.GetElementType(targetType);
var array = (Array)Utils.InstantiateArray(targetType,list.Entries.Count);
- var propertyStorage = new SimplePropertyStorage(elementType);
-
+ var propertyStorage = new SimplePropertyStorage(elementType, derivedTypeKeysDictionary);
for (int i = 0; i < list.Entries.Count; i++)
{
@@ -574,7 +584,8 @@ private bool TryFromListToArray(Type targetType, DynamoDBList list, DynamoDBFlat
return true;
}
- private bool TryFromMap(Type targetType, Document map, DynamoDBFlatConfig flatConfig, out object output)
+ private bool TryFromMap(Type targetType, Document map, DynamoDBFlatConfig flatConfig,
+ Dictionary derivedTypeKeysDictionary, out object output)
{
output = null;
@@ -587,7 +598,7 @@ private bool TryFromMap(Type targetType, Document map, DynamoDBFlatConfig flatCo
var dictionary = Utils.Instantiate(targetType);
var idictionary = dictionary as IDictionary;
- var propertyStorage = new SimplePropertyStorage(valueType);
+ var propertyStorage = new SimplePropertyStorage(valueType, derivedTypeKeysDictionary);
foreach (var kvp in map)
{
@@ -621,7 +632,17 @@ private DynamoDBEntry ToDynamoDBEntry(SimplePropertyStorage propertyStorage, obj
return entry;
}
- var type = propertyStorage.MemberType;
+ Type type;
+ string typeDiscriminator = null;
+ if (propertyStorage.DerivedTypesDictionary.ContainsKey(value.GetType()))
+ {
+ typeDiscriminator = propertyStorage.DerivedTypesDictionary[value.GetType()];
+ type = value.GetType();
+ }
+ else
+ {
+ type = propertyStorage.MemberType;
+ }
if (canReturnScalarInsteadOfList)
{
@@ -635,17 +656,18 @@ private DynamoDBEntry ToDynamoDBEntry(SimplePropertyStorage propertyStorage, obj
else
{
Document map;
- if (TryToMap(value, type, flatConfig, out map))
+ if (TryToMap(value, type, flatConfig, propertyStorage.DerivedTypesDictionary, out map))
return map;
DynamoDBList list;
- if (TryToList(value, type, flatConfig, out list))
+ if (TryToList(value, type, flatConfig, propertyStorage.DerivedTypesDictionary, out list))
return list;
- return SerializeToDocument(value, type, flatConfig);
+ return SerializeToDocument(value, type, flatConfig, typeDiscriminator);
}
}
- private bool TryToMap(object value, Type type, DynamoDBFlatConfig flatConfig, out Document output)
+ private bool TryToMap(object value, Type type, DynamoDBFlatConfig flatConfig,
+ Dictionary derivedTypesDictionary, out Document output)
{
output = null;
@@ -658,7 +680,7 @@ private bool TryToMap(object value, Type type, DynamoDBFlatConfig flatConfig, ou
return false;
output = new Document();
- SimplePropertyStorage propertyStorage = new SimplePropertyStorage(valueType);
+ SimplePropertyStorage propertyStorage = new SimplePropertyStorage(valueType,derivedTypesDictionary);
foreach (object keyValue in idictionary.Keys)
{
@@ -677,7 +699,8 @@ private bool TryToMap(object value, Type type, DynamoDBFlatConfig flatConfig, ou
}
return true;
}
- private bool TryToList(object value, Type type, DynamoDBFlatConfig flatConfig, out DynamoDBList output)
+ private bool TryToList(object value, Type type, DynamoDBFlatConfig flatConfig,
+ Dictionary derivedTypesDictionary, out DynamoDBList output)
{
if (!Utils.ImplementsInterface(type, typeof(ICollection<>)))
{
@@ -695,7 +718,9 @@ private bool TryToList(object value, Type type, DynamoDBFlatConfig flatConfig, o
}
Type elementType = Utils.GetElementType(type);
- SimplePropertyStorage propertyStorage = new SimplePropertyStorage(elementType);
+
+ //Todo IH: implement get element type for arrays with inheritance
+ SimplePropertyStorage propertyStorage = new SimplePropertyStorage(elementType,derivedTypesDictionary);
output = new DynamoDBList();
foreach (var item in enumerable)
{
@@ -703,7 +728,9 @@ private bool TryToList(object value, Type type, DynamoDBFlatConfig flatConfig, o
if (item == null)
entry = DynamoDBNull.Null;
else
+ {
entry = ToDynamoDBEntry(propertyStorage, item, flatConfig);
+ }
output.Add(entry);
}
@@ -732,7 +759,7 @@ private bool TryToScalar(object value, Type type, DynamoDBFlatConfig flatConfig,
{
try
{
- entry = SerializeToDocument(value, elementType, flatConfig);
+ entry = SerializeToDocument(value, elementType, flatConfig, typeDiscriminator: null);
return true;
}
catch { }
@@ -781,11 +808,16 @@ private object DeserializeFromDocument(Document document, Type targetType, Dynam
}
// Serializes a given value to Document
// Use only for property conversions, not for full item conversion
- private Document SerializeToDocument(object value, Type type, DynamoDBFlatConfig flatConfig)
+ private Document SerializeToDocument(object value, Type type, DynamoDBFlatConfig flatConfig,string typeDiscriminator)
{
ItemStorageConfig config = StorageConfigCache.GetConfig(type, flatConfig, conversionOnly: true);
var itemStorage = ObjectToItemStorageHelper(value, config, flatConfig, keysOnly: false, ignoreNullValues: flatConfig.IgnoreNullValues.Value);
var doc = itemStorage.Document;
+ if (typeDiscriminator != null)
+ {
+ var typeAttributeName = flatConfig.DerivedTypeAttributeName;
+ doc[typeAttributeName] = new Primitive(typeDiscriminator);
+ }
return doc;
}
@@ -810,6 +842,7 @@ private static bool TrySetValue(object instance, MemberInfo member, object value
return false;
}
}
+
private static bool TryGetValue(object instance, MemberInfo member, out object value)
{
FieldInfo fieldInfo = member as FieldInfo;
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/InternalModel.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/InternalModel.cs
index e722e9e968f0..edd908e803a6 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/InternalModel.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/InternalModel.cs
@@ -54,6 +54,18 @@ internal class SimplePropertyStorage
// Converter, if one is present
public IPropertyConverter Converter { get; protected set; }
+ public void AddDerivedType(string typeDiscriminator, Type type)
+ {
+ DerivedTypesDictionary[type] = typeDiscriminator;
+ DerivedTypeKeysDictionary[typeDiscriminator] = type;
+ }
+
+ // derived type information used for polymorphic serialization
+ public Dictionary DerivedTypesDictionary { get; private set; }
+
+ // derived type information used for polymorphic deserialization
+ public Dictionary DerivedTypeKeysDictionary { get; private set; }
+
public SimplePropertyStorage(MemberInfo member)
: this(Utils.GetType(member))
{
@@ -63,6 +75,18 @@ public SimplePropertyStorage(MemberInfo member)
public SimplePropertyStorage(Type memberType)
{
MemberType = memberType;
+ DerivedTypesDictionary = new Dictionary();
+ DerivedTypeKeysDictionary = new Dictionary();
+ }
+ public SimplePropertyStorage(Type memberType, Dictionary derivedTypesDictionary)
+ {
+ MemberType = memberType;
+ DerivedTypesDictionary = derivedTypesDictionary;
+ }
+ public SimplePropertyStorage(Type memberType, Dictionary derivedTypeKeysDictionary)
+ {
+ MemberType = memberType;
+ DerivedTypeKeysDictionary = derivedTypeKeysDictionary;
}
public override string ToString()
@@ -94,6 +118,9 @@ internal class PropertyStorage : SimplePropertyStorage
// whether to store DateTime as epoch seconds integer
public bool StoreAsEpoch { get; set; }
+ // whether to store Type Name
+ public bool TypeNameHandling { get; set; }
+
// corresponding IndexNames, if applicable
public List IndexNames { get; set; }
@@ -164,6 +191,9 @@ public void Validate(DynamoDBContext context)
if (ConverterType != null)
{
+ if (TypeNameHandling)
+ throw new InvalidOperationException("Converter for " + PropertyName + " must not be set at the same time as derived types.");
+
if (StoreAsEpoch)
throw new InvalidOperationException("Converter for " + PropertyName + " must not be set at the same time as StoreAsEpoch is set to true");
@@ -189,6 +219,7 @@ public PropertyStorage(MemberInfo member)
IndexNames = new List();
Indexes = new List();
}
+
}
///
@@ -787,7 +818,7 @@ private static void PopulateConfigFromType(ItemStorageConfig config, Type type)
if (propertyAttribute != null)
{
propertyStorage.StoreAsEpoch = propertyAttribute.StoreAsEpoch;
-
+
if (propertyAttribute.Converter != null)
propertyStorage.ConverterType = propertyAttribute.Converter;
@@ -809,7 +840,7 @@ private static void PopulateConfigFromType(ItemStorageConfig config, Type type)
{
propertyStorage.IsGSIRangeKey = true;
propertyStorage.AddIndex(gsiRangeAttribute);
- }
+ }
else
propertyStorage.IsRangeKey = true;
}
@@ -820,6 +851,13 @@ private static void PopulateConfigFromType(ItemStorageConfig config, Type type)
propertyStorage.IsLSIRangeKey = true;
propertyStorage.AddIndex(lsiRangeKeyAttribute);
}
+
+ DynamoDBDerivedTypeAttribute polymorphicAttribute = attribute as DynamoDBDerivedTypeAttribute;
+ if (polymorphicAttribute != null)
+ {
+ propertyStorage.TypeNameHandling = true;
+ propertyStorage.AddDerivedType(polymorphicAttribute.TypeDiscriminator, polymorphicAttribute.DerivedType);
+ }
}
}
diff --git a/sdk/src/Services/DynamoDBv2/Generated/.DevConfigs/bc057d5e-a710-458b-ac2e-92e0cf88b741.json b/sdk/src/Services/DynamoDBv2/Generated/.DevConfigs/bc057d5e-a710-458b-ac2e-92e0cf88b741.json
new file mode 100644
index 000000000000..55ab5e6ff6ba
--- /dev/null
+++ b/sdk/src/Services/DynamoDBv2/Generated/.DevConfigs/bc057d5e-a710-458b-ac2e-92e0cf88b741.json
@@ -0,0 +1,11 @@
+{
+ "services": [
+ {
+ "serviceName": "DynamoDBv2",
+ "type": "patch",
+ "changeLogMessages": [
+ "Implement DynamoDBDerivedTypeAttribute for polymorphism support on save and load data."
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs b/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs
index 5763cf7631ae..16b930c28f81 100644
--- a/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs
+++ b/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs
@@ -10,6 +10,7 @@
using Amazon.DynamoDBv2.Model;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.DataModel;
+using System.Threading.Tasks;
namespace AWSSDK_DotNet.IntegrationTests.Tests.DynamoDB
{
@@ -390,6 +391,74 @@ public void TestContext_RetrieveDateTimeInUtc_OperationConfig(bool retrieveDateT
Assert.AreEqual(employee.Age, storedEmployee.Age);
}
+ ///
+ /// Tests that the DynamoDB operations can read and write polymorphic items.
+ ///
+ ///
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public async Task TestContext_SaveAndLoad_WithDerivedTypeItems()
+ {
+ CleanupTables();
+ TableCache.Clear();
+
+ var a1 = new A { Name = "A1", MyPropA = 1};
+ var b1 = new B { Name = "B1", MyPropA = 2,MyPropB = 3};
+
+ Guid id = Guid.NewGuid();
+
+ var model = new ModelA
+ {
+ Id = id,
+ MyType = b1,
+ MyClasses = new List { a1, b1 },
+ DictionaryClasses = new Dictionary()
+ {
+ {"A",a1},
+ {"B",b1}
+ }
+ };
+
+ await Context.SaveAsync(model);
+
+ var storedModel = await Context.LoadAsync(id);
+ Assert.AreEqual(model.Id, storedModel.Id);
+ Assert.AreEqual(model.MyType.GetType(), storedModel.MyType.GetType());
+ Assert.AreEqual(model.MyClasses.Count, storedModel.MyClasses.Count);
+ Assert.AreEqual(model.MyClasses[0].GetType(), storedModel.MyClasses[0].GetType());
+ Assert.AreEqual(model.MyClasses[1].GetType(), storedModel.MyClasses[1].GetType());
+ Assert.AreEqual(model.DictionaryClasses["A"].GetType(), storedModel.DictionaryClasses["A"].GetType());
+ Assert.AreEqual(model.DictionaryClasses["B"].GetType(), storedModel.DictionaryClasses["B"].GetType());
+ }
+
+ ///
+ /// Tests that the DynamoDB operations can read and write polymorphic items.
+ ///
+ ///
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public async Task TestContext_TransactWriteAndLoad_WithDerivedTypeItems()
+ {
+ CleanupTables();
+ TableCache.Clear();
+
+ var model = CreateNestedTypeItem(out var id);
+
+ var transactWrite = Context.CreateTransactWrite();
+ transactWrite.AddSaveItem(model);
+ await transactWrite.ExecuteAsync();
+
+ var storedModel = await Context.LoadAsync(id);
+ Assert.AreEqual(model.Id, storedModel.Id);
+ Assert.AreEqual(model.MyType.GetType(), storedModel.MyType.GetType());
+ Assert.AreEqual(model.MyClasses.Count, storedModel.MyClasses.Count);
+ Assert.AreEqual(model.MyClasses[0].GetType(), storedModel.MyClasses[0].GetType());
+ Assert.AreEqual(model.MyClasses[1].GetType(), storedModel.MyClasses[1].GetType());
+ Assert.AreEqual(model.DictionaryClasses["A"].GetType(), storedModel.DictionaryClasses["A"].GetType());
+ Assert.AreEqual(model.DictionaryClasses["B"].GetType(), storedModel.DictionaryClasses["B"].GetType());
+ }
+
+
///
/// Runs the same object-mapper integration tests as ,
/// but using table definitions created by instead of the internal call
@@ -1723,25 +1792,45 @@ private void TestOtherContextOperations()
Assert.AreEqual(employee1.Data.Length, doc["Data"].AsByteArray().Length);
}
+ private ModelA CreateNestedTypeItem(out Guid id)
+ {
+ var a1 = new A { Name = "A1", MyPropA = 1 };
+ var b1 = new B { Name = "B1", MyPropA = 2, MyPropB = 3 };
+
+ id = Guid.NewGuid();
+
+ var model = new ModelA
+ {
+ Id = id,
+ MyType = b1,
+ MyClasses = new List { a1, b1 },
+ DictionaryClasses = new Dictionary()
+ {
+ {"A",a1},
+ {"B",b1}
+ }
+ };
+ return model;
+ }
#region OPM definitions
public enum Status : long
{
- Active = 256,
- Inactive = 1024,
- Upcoming = 9999,
- Obsolete = -10,
- Removed = 42
+ Active = 256,
+ Inactive = 1024,
+ Upcoming = 9999,
+ Obsolete = -10,
+ Removed = 42
}
[Flags]
public enum Support
{
- Windows = 1 << 0,
- iOS = 1 << 1,
- Unix = 1 << 2,
- Abacus = 1 << 3,
+ Windows = 1 << 0,
+ iOS = 1 << 1,
+ Unix = 1 << 2,
+ Abacus = 1 << 3,
}
public class StatusConverter : IPropertyConverter
@@ -2063,6 +2152,35 @@ public object FromEntry(DynamoDBEntry entry)
}
}
+ public class A
+ {
+ public string Name { get; set; }
+
+ public int MyPropA { get; set; }
+ }
+
+ public class B : A
+ {
+ public int MyPropB { get; set; }
+ }
+
+ [DynamoDBTable("TestTable")]
+ public class ModelA
+ {
+ [DynamoDBHashKey]
+ public Guid Id { get; set; }
+
+ [DynamoDBDerivedType("B", typeof(B))]
+ public A MyType { get; set; }
+
+ [DynamoDBDerivedType("B", typeof(B))]
+ [DynamoDBProperty("test")]
+ public List MyClasses { get; set; }
+
+
+ [DynamoDBDerivedType("B", typeof(B))]
+ public Dictionary DictionaryClasses { get; set; }
+ }
#endregion
}
}
diff --git a/sdk/test/Services/DynamoDBv2/IntegrationTests/DynamoDBTestsBase.cs b/sdk/test/Services/DynamoDBv2/IntegrationTests/DynamoDBTestsBase.cs
index 742411330d32..812e03453ae6 100644
--- a/sdk/test/Services/DynamoDBv2/IntegrationTests/DynamoDBTestsBase.cs
+++ b/sdk/test/Services/DynamoDBv2/IntegrationTests/DynamoDBTestsBase.cs
@@ -86,6 +86,7 @@ private static void ClientBeforeRequestEvent(object sender, Amazon.Runtime.Reque
public void CleanupTables()
{
ClearTable(hashTableName);
+ ClearTable(nestedTableName);
ClearTable(hashRangeTableName);
ClearTable(numericHashRangeTableName);
}
@@ -102,6 +103,7 @@ public static void CreateContext(DynamoDBEntryConversion conversion, bool isEmpt
Context = new DynamoDBContext(Client, config);
}
+ public static string nestedTableName;
public static string hashTableName;
public static string hashRangeTableName;
public static string numericHashRangeTableName;
@@ -148,15 +150,22 @@ public static void ClearTable(string tableName)
public static void CreateTestTables()
{
+ nestedTableName = TableNamePrefix + "NestedTable";
hashTableName = TableNamePrefix + "HashTable";
hashRangeTableName = TableNamePrefix + "HashRangeTable";
numericHashRangeTableName = TableNamePrefix + "NumericHashRangeTable";
+ bool createTestTable = true;
bool createHashTable = true;
bool createHashRangeTable = true;
bool createNumericHashRangeTable = true;
if (ReuseTables)
{
+ if (GetStatus(nestedTableName) != null)
+ {
+ WaitForTableStatus(nestedTableName, TableStatus.ACTIVE);
+ createTestTable = false;
+ }
if (GetStatus(hashTableName) != null)
{
WaitForTableStatus(hashTableName, TableStatus.ACTIVE);
@@ -174,6 +183,28 @@ public static void CreateTestTables()
}
}
+ if (createTestTable)
+ {
+ // Create hash-key table with global index
+ Client.CreateTable(new CreateTableRequest
+ {
+ TableName = nestedTableName,
+ AttributeDefinitions = new List
+ {
+ new AttributeDefinition { AttributeName = "Id", AttributeType = ScalarAttributeType.S },
+ },
+ KeySchema = new List
+ {
+ new KeySchemaElement { KeyType = KeyType.HASH, AttributeName = "Id" }
+ },
+ BillingMode = BillingMode.PAY_PER_REQUEST
+ });
+ CreatedTables.Add(nestedTableName);
+
+ // Wait for table to be ready
+ WaitForTableStatus(nestedTableName, TableStatus.ACTIVE);
+ }
+
if (createHashTable)
{
// Create hash-key table with global index
@@ -294,6 +325,7 @@ public static void CreateTestTables()
// Make sure TTL is enabled for the tables and is on the correct attribute
+ EnsureTTL(nestedTableName);
EnsureTTL(hashTableName);
EnsureTTL(hashRangeTableName);
EnsureTTL(numericHashRangeTableName);