diff --git a/sdk/src/Services/DynamoDBv2/Custom/Conversion/DynamoDBEntryConversion.cs b/sdk/src/Services/DynamoDBv2/Custom/Conversion/DynamoDBEntryConversion.cs index dca832f72762..bfab62b011c3 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/Conversion/DynamoDBEntryConversion.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/Conversion/DynamoDBEntryConversion.cs @@ -15,11 +15,11 @@ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using Amazon.DynamoDBv2.DataModel; using Amazon.DynamoDBv2.DocumentModel; -using Amazon.Util.Internal; #if NETSTANDARD using Amazon.Runtime.Internal.Util; @@ -71,9 +71,6 @@ internal enum ConversionSchema /// A collection of converters capable of converting between /// .NET and DynamoDB objects. /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public class DynamoDBEntryConversion { #region Static members @@ -360,6 +357,7 @@ internal IEnumerable ConvertToEntries(IEnumerable values) //foreach (var value in values) // yield return ConvertToEntry(value); } + internal IEnumerable ConvertFromEntries(Type elementType, IEnumerable entries) { if (entries == null) throw new ArgumentNullException("entries"); @@ -368,6 +366,14 @@ internal IEnumerable ConvertFromEntries(Type elementType, IEnumerable ConvertFromEntries(IEnumerable entries) + { + if (entries == null) throw new ArgumentNullException("entries"); + + foreach (var entry in entries) + yield return ConvertFromEntry(entry); + } + internal PrimitiveList ItemsToPrimitiveList(IEnumerable items) { var inputType = items.GetType(); @@ -384,59 +390,24 @@ internal PrimitiveList ItemsToPrimitiveList(IEnumerable items) private ConverterCache ConverterCache = new ConverterCache(); private ConversionSchema OriginalConversion; - internal void AddConverter(Converter converter) + internal void AddConverterFactory(ConverterFactory factory) { - ConverterCache.AddConverter(converter, this); + if (IsImmutable) + throw new InvalidOperationException("Adding converters to immutable conversion is not supported. The conversion must be cloned first."); + + ConverterCache.AddConverterFactory(factory); } private void SetV1Converters() { - AddConverter(new ByteConverterV1()); - AddConverter(new SByteConverterV1()); - AddConverter(new UInt16ConverterV1()); - AddConverter(new Int16ConverterV1()); - AddConverter(new UInt32ConverterV1()); - AddConverter(new Int32ConverterV1()); - AddConverter(new UInt64ConverterV1()); - AddConverter(new Int64ConverterV1()); - AddConverter(new SingleConverterV1()); - AddConverter(new DoubleConverterV1()); - AddConverter(new DecimalConverterV1()); - AddConverter(new CharConverterV1()); - AddConverter(new StringConverterV1()); - AddConverter(new DateTimeConverterV1()); - AddConverter(new GuidConverterV1()); - AddConverter(new BytesConverterV1()); - AddConverter(new MemoryStreamConverterV1()); - AddConverter(new EnumConverterV1()); - AddConverter(new BoolConverterV1()); - AddConverter(new PrimitiveCollectionConverterV1()); - AddConverter(new DictionaryConverterV1()); + AddConverterFactory(new DefaultConverterFactoryV1(this)); + AddConverterFactory(new CollectionConverterFactoryV1(this)); } private void SetV2Converters() { - AddConverter(new ByteConverterV2()); - AddConverter(new SByteConverterV2()); - AddConverter(new UInt16ConverterV2()); - AddConverter(new Int16ConverterV2()); - AddConverter(new UInt32ConverterV2()); - AddConverter(new Int32ConverterV2()); - AddConverter(new UInt64ConverterV2()); - AddConverter(new Int64ConverterV2()); - AddConverter(new SingleConverterV2()); - AddConverter(new DoubleConverterV2()); - AddConverter(new DecimalConverterV2()); - AddConverter(new CharConverterV2()); - AddConverter(new StringConverterV2()); - AddConverter(new DateTimeConverterV2()); - AddConverter(new GuidConverterV2()); - AddConverter(new BytesConverterV2()); - AddConverter(new MemoryStreamConverterV2()); - AddConverter(new DictionaryConverterV2()); - AddConverter(new EnumConverterV2()); - AddConverter(new BoolConverterV2()); - AddConverter(new CollectionConverterV2()); + AddConverterFactory(new DefaultConverterFactoryV2(this)); + AddConverterFactory(new CollectionConverterFactoryV2(this)); } // Converts items to Primitives. @@ -468,14 +439,30 @@ private IEnumerable ToPrimitives(IEnumerable items, Type elementType) #endregion } - internal abstract class Converter + internal abstract class ConverterFactory { - /// - /// Returns all types for which it can be used. - /// - /// - public abstract IEnumerable GetTargetTypes(); + private readonly DynamoDBEntryConversion _conversion; + + protected ConverterFactory(DynamoDBEntryConversion conversion) + { + _conversion = conversion; + } + + public Converter GetConverter(Type type) + { + var converter = CreateConverter(type); + + if(converter != null) + converter.Conversion = _conversion; + + return converter; + } + protected abstract Converter CreateConverter(Type type); + } + + internal abstract class Converter + { /// /// Conversion that this converter is part of. /// This field is set by DynamoDBEntryConversion when the Converter @@ -651,19 +638,6 @@ public virtual bool TryFrom(Document d, Type targetType, out object result) internal abstract class Converter : Converter { - public override IEnumerable GetTargetTypes() - { - var type = typeof(T); - yield return type; - - if (type.IsValueType) - { - //yield return typeof(Nullable); - var nullableType = typeof(Nullable<>).MakeGenericType(type); - yield return nullableType; - } - } - public override bool TryTo(object value, out DynamoDBBool b) { return TryTo((T)value, out b); @@ -714,60 +688,60 @@ protected virtual bool TryTo(T value, out Document d) public override bool TryFrom(DynamoDBBool b, Type targetType, out object result) { T t; - var output = TryFrom(b, targetType, out t); + var output = TryFrom(b, out t); result = t; return output; } public override bool TryFrom(Primitive p, Type targetType, out object result) { T t; - var output = TryFrom(p, targetType, out t); + var output = TryFrom(p, out t); result = t; return output; } public override bool TryFrom(PrimitiveList pl, Type targetType, out object result) { T t; - var output = TryFrom(pl, targetType, out t); + var output = TryFrom(pl, out t); result = t; return output; } public override bool TryFrom(DynamoDBList l, Type targetType, out object result) { T t; - var output = TryFrom(l, targetType, out t); + var output = TryFrom(l, out t); result = t; return output; } public override bool TryFrom(Document d, Type targetType, out object result) { T t; - var output = TryFrom(d, targetType, out t); + var output = TryFrom(d, out t); result = t; return output; } - protected virtual bool TryFrom(DynamoDBBool b, Type targetType, out T result) + protected virtual bool TryFrom(DynamoDBBool b, out T result) { result = default(T); return false; } - protected virtual bool TryFrom(Primitive p, Type targetType, out T result) + protected virtual bool TryFrom(Primitive p, out T result) { result = default(T); return false; } - protected virtual bool TryFrom(PrimitiveList pl, Type targetType, out T result) + protected virtual bool TryFrom(PrimitiveList pl, out T result) { result = default(T); return false; } - protected virtual bool TryFrom(DynamoDBList l, Type targetType, out T result) + protected virtual bool TryFrom(DynamoDBList l, out T result) { result = default(T); return false; } - protected virtual bool TryFrom(Document d, Type targetType, out T result) + protected virtual bool TryFrom(Document d, out T result) { result = default(T); return false; @@ -777,29 +751,20 @@ protected virtual bool TryFrom(Document d, Type targetType, out T result) internal class ConverterCache { private static Type EnumType = typeof(Enum); - private Dictionary Cache = new Dictionary(); + private readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); + private readonly List Factories = new List(); public bool HasConverter(Type type) { - Converter converter; - return TryGetConverter(type, out converter); + return TryGetConverter(type, out _); } - public void AddConverter(Converter converter, DynamoDBEntryConversion conversion) - { - if (converter == null) - throw new ArgumentNullException("converter"); - if (conversion == null) - throw new ArgumentNullException("conversion"); - if (conversion.IsImmutable) - throw new InvalidOperationException("Adding converters to immutable conversion is not supported. The conversion must be cloned first."); + public void AddConverterFactory(ConverterFactory factory) + { + if (factory == null) + throw new ArgumentNullException(nameof(factory)); - converter.Conversion = conversion; - var types = converter.GetTargetTypes(); - foreach (var type in types) - { - Cache[type] = converter; - } + Factories.Add(factory); } public Converter GetConverter(Type type) @@ -817,9 +782,23 @@ public bool TryGetConverter(Type type, out Converter converter) // all enums use the same converter, one for Enum if (type.IsEnum) - return Cache.TryGetValue(EnumType, out converter); + type = EnumType; - return Cache.TryGetValue(type, out converter); + if (Cache.TryGetValue(type, out converter)) + return converter != null; + + foreach (var factory in Factories) + { + converter = factory.GetConverter(type); + if (converter != null) + { + Cache[type] = converter; + return true; + } + } + + Cache[type] = null; + return false; } } } diff --git a/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV1.cs b/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV1.cs index d0d0d16f770d..b28ee42f5375 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV1.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV1.cs @@ -1,38 +1,135 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ -using Amazon.DynamoDBv2.DataModel; -using Amazon.DynamoDBv2.DocumentModel; -using Amazon.Runtime.Internal.Util; -using Amazon.Util; using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; -using System.Text; +using Amazon.DynamoDBv2.DataModel; +using Amazon.DynamoDBv2.DocumentModel; +using Amazon.Util; + +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace Amazon.DynamoDBv2 { - #region Basic converters + #region Converter factories + + internal class DefaultConverterFactoryV1 : ConverterFactory + { + public DefaultConverterFactoryV1(DynamoDBEntryConversion conversion) : base(conversion) + { + } + + protected override Converter CreateConverter(Type type) + { + if (Nullable.GetUnderlyingType(type) is var underlyingType && underlyingType != null) + type = underlyingType; + + if (type == typeof(bool)) return new BoolConverterV1(); + if (type == typeof(byte)) return new ByteConverterV1(); + if (type == typeof(sbyte)) return new SByteConverterV1(); + if (type == typeof(ushort)) return new UInt16ConverterV1(); + if (type == typeof(short)) return new Int16ConverterV1(); + if (type == typeof(uint)) return new UInt32ConverterV1(); + if (type == typeof(int)) return new Int32ConverterV1(); + if (type == typeof(ulong)) return new UInt64ConverterV1(); + if (type == typeof(long)) return new Int64ConverterV1(); + if (type == typeof(float)) return new SingleConverterV1(); + if (type == typeof(double)) return new DoubleConverterV1(); + if (type == typeof(decimal)) return new DecimalConverterV1(); + if (type == typeof(char)) return new CharConverterV1(); + if (type == typeof(string)) return new StringConverterV1(); + if (type == typeof(DateTime)) return new DateTimeConverterV1(); + if (type == typeof(Guid)) return new GuidConverterV1(); + if (type == typeof(byte[])) return new BytesConverterV1(); + if (type == typeof(MemoryStream)) return new MemoryStreamConverterV1(); + if (type == typeof(Enum) || type.IsEnum) return new EnumConverterV1(); + if (type == typeof(Dictionary)) return new DictionaryConverterV1(); + + return null; + } + } + + internal abstract class CollectionConverterFactory : ConverterFactory + { + private readonly IEnumerable _elementTypes; + + protected CollectionConverterFactory(DynamoDBEntryConversion conversion, IEnumerable elementTypes) : base(conversion) + { + _elementTypes = elementTypes; + } + + protected override Converter CreateConverter(Type type) + { + if (!IsSupportedCollectionType(type, out var elementType)) + return null; + + if (!_elementTypes.Contains(elementType)) + return null; + + return CreateConverter(elementType, type); + } + + protected abstract Converter CreateConverter(Type elementType, Type collectionType); + + private static bool IsSupportedCollectionType(Type type, out Type elementType) + { + if (type.IsArray) + { + elementType = type.GetElementType(); + return true; + } + + if (type.IsGenericType) + { + var definition = type.GetGenericTypeDefinition(); + if (definition == typeof(List<>) || definition == typeof(HashSet<>)) + { + elementType = type.GetGenericArguments()[0]; + return true; + } + } + + elementType = null; + return false; + } + } + + internal class CollectionConverterFactoryV1 : CollectionConverterFactory + { + public CollectionConverterFactoryV1(DynamoDBEntryConversion conversion) : base(conversion, Utils.PrimitiveTypes) { } #if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", + Justification = "MakeGenericType is safe here since we support reference types only, so fully shareable code is generated.")] #endif + protected override Converter CreateConverter(Type elementType, Type collectionType) + { + var genericType = typeof(PrimitiveCollectionConverterV1<,>).MakeGenericType(elementType, collectionType); + return Activator.CreateInstance(genericType) as Converter; + } + } + + #endregion + + #region Basic converters + internal class ByteConverterV1 : Converter { protected override bool TryTo(byte value, out Primitive p) @@ -40,15 +137,12 @@ protected override bool TryTo(byte value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out byte result) + protected override bool TryFrom(Primitive p, out byte result) { return byte.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class SByteConverterV1 : Converter { protected override bool TryTo(sbyte value, out Primitive p) @@ -56,15 +150,12 @@ protected override bool TryTo(sbyte value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out sbyte result) + protected override bool TryFrom(Primitive p, out sbyte result) { return SByte.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class UInt16ConverterV1 : Converter { protected override bool TryTo(ushort value, out Primitive p) @@ -72,15 +163,12 @@ protected override bool TryTo(ushort value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out ushort result) + protected override bool TryFrom(Primitive p, out ushort result) { return UInt16.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class Int16ConverterV1 : Converter { protected override bool TryTo(short value, out Primitive p) @@ -88,15 +176,12 @@ protected override bool TryTo(short value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out short result) + protected override bool TryFrom(Primitive p, out short result) { return Int16.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class UInt32ConverterV1 : Converter { protected override bool TryTo(uint value, out Primitive p) @@ -104,15 +189,12 @@ protected override bool TryTo(uint value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out uint result) + protected override bool TryFrom(Primitive p, out uint result) { return UInt32.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class Int32ConverterV1 : Converter { protected override bool TryTo(int value, out Primitive p) @@ -120,15 +202,12 @@ protected override bool TryTo(int value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out int result) + protected override bool TryFrom(Primitive p, out int result) { return Int32.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class UInt64ConverterV1 : Converter { protected override bool TryTo(ulong value, out Primitive p) @@ -136,15 +215,12 @@ protected override bool TryTo(ulong value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out ulong result) + protected override bool TryFrom(Primitive p, out ulong result) { return UInt64.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class Int64ConverterV1 : Converter { protected override bool TryTo(long value, out Primitive p) @@ -152,15 +228,12 @@ protected override bool TryTo(long value, out Primitive p) p = new Primitive(value.ToString("d", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out long result) + protected override bool TryFrom(Primitive p, out long result) { return Int64.TryParse(p.StringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class SingleConverterV1 : Converter { protected override bool TryTo(float value, out Primitive p) @@ -168,15 +241,12 @@ protected override bool TryTo(float value, out Primitive p) p = new Primitive(value.ToString("r", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out float result) + protected override bool TryFrom(Primitive p, out float result) { return Single.TryParse(p.StringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DoubleConverterV1 : Converter { protected override bool TryTo(double value, out Primitive p) @@ -184,15 +254,11 @@ protected override bool TryTo(double value, out Primitive p) p = new Primitive(value.ToString("r", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out double result) + protected override bool TryFrom(Primitive p, out double result) { return Double.TryParse(p.StringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out result); } } - -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DecimalConverterV1 : Converter { protected override bool TryTo(decimal value, out Primitive p) @@ -200,15 +266,12 @@ protected override bool TryTo(decimal value, out Primitive p) p = new Primitive(value.ToString("g", CultureInfo.InvariantCulture), DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out decimal result) + protected override bool TryFrom(Primitive p, out decimal result) { return Decimal.TryParse(p.StringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class CharConverterV1 : Converter { protected override bool TryTo(char value, out Primitive p) @@ -216,15 +279,12 @@ protected override bool TryTo(char value, out Primitive p) p = new Primitive(value.ToString(), DynamoDBEntryType.String); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out char result) + protected override bool TryFrom(Primitive p, out char result) { return Char.TryParse(p.StringValue, out result); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class StringConverterV1 : Converter { protected override bool TryTo(string value, out Primitive p) @@ -232,16 +292,13 @@ protected override bool TryTo(string value, out Primitive p) p = new Primitive(value, DynamoDBEntryType.String); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out string result) + protected override bool TryFrom(Primitive p, out string result) { result = p.StringValue; return (result != null); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DateTimeConverterV1 : Converter { protected override bool TryTo(DateTime value, out Primitive p) @@ -250,7 +307,7 @@ protected override bool TryTo(DateTime value, out Primitive p) p = new Primitive(utc.ToString(AWSSDKUtils.ISO8601DateFormat, CultureInfo.InvariantCulture), DynamoDBEntryType.String); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out DateTime result) + protected override bool TryFrom(Primitive p, out DateTime result) { if (DateTime.TryParseExact(p.StringValue, AWSSDKUtils.ISO8601DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result)) { @@ -260,9 +317,6 @@ protected override bool TryFrom(Primitive p, Type targetType, out DateTime resul } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class GuidConverterV1 : Converter { protected override bool TryTo(Guid value, out Primitive p) @@ -270,16 +324,13 @@ protected override bool TryTo(Guid value, out Primitive p) p = new Primitive(value.ToString("D"), DynamoDBEntryType.String); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out Guid result) + protected override bool TryFrom(Primitive p, out Guid result) { result = new Guid(p.StringValue); return true; } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class BytesConverterV1 : Converter { protected override bool TryTo(byte[] value, out Primitive p) @@ -287,16 +338,13 @@ protected override bool TryTo(byte[] value, out Primitive p) p = new Primitive(value); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out byte[] result) + protected override bool TryFrom(Primitive p, out byte[] result) { result = p.Value as byte[]; return (result != null); } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class MemoryStreamConverterV1 : Converter { protected override bool TryTo(MemoryStream value, out Primitive p) @@ -304,7 +352,7 @@ protected override bool TryTo(MemoryStream value, out Primitive p) p = new Primitive(value); return true; } - protected override bool TryFrom(Primitive p, Type targetType, out MemoryStream result) + protected override bool TryFrom(Primitive p, out MemoryStream result) { var bytes = p.Value as byte[]; if (bytes == null) @@ -318,12 +366,9 @@ protected override bool TryFrom(Primitive p, Type targetType, out MemoryStream r } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif - internal class EnumConverterV1 : Converter + internal class EnumConverterV1 : Converter { - protected override bool TryTo(Enum value, out Primitive p) + public override bool TryTo(object value, out Primitive p) { p = null; @@ -344,7 +389,8 @@ protected override bool TryTo(Enum value, out Primitive p) var succeeded = (p != null); return succeeded; } - protected override bool TryFrom(Primitive p, Type targetType, out Enum result) + + public override bool TryFrom(Primitive p, Type targetType, out object result) { result = null; @@ -401,9 +447,6 @@ private static Enum ConvertEnum(string s, Type targetType) /// A boolean converter which reads booleans as N or BOOL types, /// but writes out N type (1 if true, 0 if false). /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class BoolConverterV1 : Converter { protected override bool TryTo(bool value, out Primitive p) @@ -411,96 +454,40 @@ protected override bool TryTo(bool value, out Primitive p) p = new Primitive(value ? "1" : "0", DynamoDBEntryType.Numeric); return true; } - protected override bool TryFrom(DynamoDBBool b, Type targetType, out bool result) + protected override bool TryFrom(DynamoDBBool b, out bool result) { result = b.Value; return true; } - protected override bool TryFrom(Primitive p, Type targetType, out bool result) + protected override bool TryFrom(Primitive p, out bool result) { result = !p.StringValue.Equals("0", StringComparison.OrdinalIgnoreCase); return true; } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif - internal abstract class CollectionConverter : Converter - { - private IEnumerable targetTypes; - private static IEnumerable GetTargetTypes(IEnumerable memberTypes) - { - var listType = typeof(List<>); - var setType = typeof(HashSet<>); - - foreach (var pt in memberTypes) - { - // typeof(T[]), - if (pt != typeof(byte)) - { - yield return pt.MakeArrayType(); - } - // typeof(List), - yield return listType.MakeGenericType(pt); - // typeof(HashSet), - yield return setType.MakeGenericType(pt); - } - } - public CollectionConverter(IEnumerable memberTypes) - { - targetTypes = GetTargetTypes(memberTypes); - } - - public override IEnumerable GetTargetTypes() - { - return targetTypes; - } - - protected bool EntriesToCollection(Type targetType, Type elementType, IEnumerable entries, out object result) - { - var items = Conversion.ConvertFromEntries(elementType, entries); - return Utils.ItemsToCollection(targetType, items, out result); - } - } - /// /// A collection converter which reads both sets of collections (sets and lists) /// and writes out sets (NS, SS, BS) /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif - internal class PrimitiveCollectionConverterV1 : CollectionConverter + internal class PrimitiveCollectionConverterV1 : Converter where TCollection : ICollection { - public PrimitiveCollectionConverterV1() - : base(Utils.PrimitiveTypes) - { } - - public override bool TryTo(object value, out PrimitiveList pl) + protected override bool TryTo(TCollection value, out PrimitiveList pl) { - var items = value as IEnumerable; - if (items != null) - { - pl = Conversion.ItemsToPrimitiveList(items); - return true; - } - - pl = null; - return false; + pl = Conversion.ItemsToPrimitiveList(value); + return true; } - public override bool TryFrom(PrimitiveList pl, Type targetType, out object result) + protected override bool TryFrom(PrimitiveList pl, out TCollection result) { - var elementType = Utils.GetPrimitiveElementType(targetType); - var primitives = pl.Entries; - return EntriesToCollection(targetType, elementType, pl.Entries.Cast(), out result); + var items = Conversion.ConvertFromEntries(pl.Entries); + return Utils.ItemsToCollection(items, out result); } - public override bool TryFrom(DynamoDBList l, Type targetType, out object result) + + protected override bool TryFrom(DynamoDBList l, out TCollection result) { - var elementType = Utils.GetPrimitiveElementType(targetType); - var entries = l.Entries; - return EntriesToCollection(targetType, elementType, entries, out result); + var items = Conversion.ConvertFromEntries(l.Entries); + return Utils.ItemsToCollection(items, out result); } } @@ -509,23 +496,15 @@ public override bool TryFrom(DynamoDBList l, Type targetType, out object result) /// Converts from Dictionary{string,object} to DynamoDBEntry. /// Does NOT convert from DynamoDBEntry to Dictionary{string,object}. /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DictionaryConverterV1 : Converter { - public override IEnumerable GetTargetTypes() - { - yield return typeof(Dictionary); - } - public override bool TryTo(object value, out Document d) { var items = value as IDictionary; if (items != null) { d = new Document(); - foreach(var kvp in items) + foreach (var kvp in items) { string name = kvp.Key; object item = kvp.Value; diff --git a/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV2.cs b/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV2.cs index 7ea4a6e02331..d3ac90db22a9 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV2.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/Conversion/SchemaV2.cs @@ -1,143 +1,158 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ -using Amazon.DynamoDBv2.DataModel; -using Amazon.DynamoDBv2.DocumentModel; -using Amazon.Util.Internal; using System; -using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; +using Amazon.DynamoDBv2.DataModel; +using Amazon.DynamoDBv2.DocumentModel; + +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace Amazon.DynamoDBv2 { - #region Same converter behavior as V1 + #region Converter factories + + internal class DefaultConverterFactoryV2 : ConverterFactory + { + public DefaultConverterFactoryV2(DynamoDBEntryConversion conversion) : base(conversion) + { + } + + protected override Converter CreateConverter(Type type) + { + if (Nullable.GetUnderlyingType(type) is var underlyingType && underlyingType != null) + type = underlyingType; + + if (type == typeof(bool)) return new BoolConverterV2(); + if (type == typeof(byte)) return new ByteConverterV2(); + if (type == typeof(sbyte)) return new SByteConverterV2(); + if (type == typeof(ushort)) return new UInt16ConverterV2(); + if (type == typeof(short)) return new Int16ConverterV2(); + if (type == typeof(uint)) return new UInt32ConverterV2(); + if (type == typeof(int)) return new Int32ConverterV2(); + if (type == typeof(ulong)) return new UInt64ConverterV2(); + if (type == typeof(long)) return new Int64ConverterV2(); + if (type == typeof(float)) return new SingleConverterV2(); + if (type == typeof(double)) return new DoubleConverterV2(); + if (type == typeof(decimal)) return new DecimalConverterV2(); + if (type == typeof(char)) return new CharConverterV2(); + if (type == typeof(string)) return new StringConverterV2(); + if (type == typeof(DateTime)) return new DateTimeConverterV2(); + if (type == typeof(Guid)) return new GuidConverterV2(); + if (type == typeof(byte[])) return new BytesConverterV2(); + if (type == typeof(MemoryStream)) return new MemoryStreamConverterV2(); + if (type == typeof(Enum) || type.IsEnum) return new EnumConverterV2(); + if (type == typeof(Dictionary)) return new DictionaryConverterV2(); + + return null; + } + } + + internal class CollectionConverterFactoryV2 : CollectionConverterFactory + { + public CollectionConverterFactoryV2(DynamoDBEntryConversion conversion) : base(conversion, Utils.PrimitiveTypes) { } #if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", + Justification = "MakeGenericType is safe here since we support reference types only, so fully shareable code is generated.")] #endif - internal class ByteConverterV2 : ByteConverterV1 - { } + protected override Converter CreateConverter(Type elementType, Type collectionType) + { + var genericType = typeof(CollectionConverterV2<,>).MakeGenericType(elementType, collectionType); + return Activator.CreateInstance(genericType) as Converter; + } + } + + internal class DynamoDBListConverterFactory : CollectionConverterFactory + { + public DynamoDBListConverterFactory(DynamoDBEntryConversion conversion) + : base(conversion, new[] { typeof(String), typeof(MemoryStream), typeof(byte[]) }) { } #if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", + Justification = "MakeGenericType is safe here since we support reference types only, so fully shareable code is generated.")] #endif + protected override Converter CreateConverter(Type elementType, Type collectionType) + { + var genericType = typeof(DynamoDBListConverter<,>).MakeGenericType(elementType, collectionType); + return Activator.CreateInstance(genericType) as Converter; + } + } + +#endregion + + + #region Same converter behavior as V1 + + internal class ByteConverterV2 : ByteConverterV1 + { } + internal class SByteConverterV2 : SByteConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class UInt16ConverterV2 : UInt16ConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class Int16ConverterV2 : Int16ConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class UInt32ConverterV2 : UInt32ConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class Int32ConverterV2 : Int32ConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class UInt64ConverterV2 : UInt64ConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class Int64ConverterV2 : Int64ConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class SingleConverterV2 : SingleConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DoubleConverterV2 : DoubleConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DecimalConverterV2 : DecimalConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class CharConverterV2 : CharConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class StringConverterV2 : StringConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DateTimeConverterV2 : DateTimeConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class GuidConverterV2 : GuidConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class BytesConverterV2 : BytesConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class MemoryStreamConverterV2 : MemoryStreamConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class DictionaryConverterV2 : DictionaryConverterV1 { } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class EnumConverterV2 : EnumConverterV1 { } @@ -148,9 +163,6 @@ internal class EnumConverterV2 : EnumConverterV1 /// but writes out BOOL type. /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class BoolConverterV2 : BoolConverterV1 { protected override bool TryTo(bool value, out DynamoDBBool b) @@ -169,34 +181,22 @@ protected override bool TryTo(bool value, out Primitive p) /// A collection converter which reads both sets of collections (sets and lists) /// but writes out different types depending on input: /// HashSet input - converts to a DynamoDB set (NS, SS, BS) - /// Any other IEnumerable input - converts to a DynamoDB list (L) + /// Array or List input - converts to a DynamoDB list (L) /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif - internal class CollectionConverterV2 : PrimitiveCollectionConverterV1 + internal class CollectionConverterV2 : PrimitiveCollectionConverterV1 where TCollection : ICollection { - private static Type setTypeInfo = typeof(HashSet<>); - private static Type enumerableType = typeof(IEnumerable<>); - /// /// If value is HashSet{T}, converts the items to PrimitiveList /// /// /// /// - public override bool TryTo(object value, out PrimitiveList pl) + protected override bool TryTo(TCollection value, out PrimitiveList pl) { - var inputType = value.GetType(); - - if (inputType.IsGenericType) + if (typeof(TCollection) == typeof(HashSet)) { - var genericType = inputType.GetGenericTypeDefinition(); - if (setTypeInfo.IsAssignableFrom(genericType)) - { - pl = Conversion.ItemsToPrimitiveList(value as IEnumerable); - return true; - } + pl = Conversion.ItemsToPrimitiveList(value); + return true; } pl = null; @@ -210,14 +210,11 @@ public override bool TryTo(object value, out PrimitiveList pl) /// /// /// - public override bool TryTo(object value, out DynamoDBList l) + protected override bool TryTo(TCollection value, out DynamoDBList l) { - var inputType = value.GetType(); - - if (DataModel.Utils.ImplementsInterface(inputType, enumerableType)) + if (typeof(TCollection) != typeof(HashSet)) { - var elementType = Utils.GetElementType(inputType); - var entries = Conversion.ConvertToEntries(elementType, value as IEnumerable); + var entries = Conversion.ConvertToEntries(value); l = new DynamoDBList(entries); return true; } @@ -227,43 +224,23 @@ public override bool TryTo(object value, out DynamoDBList l) } } -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif - internal class DynamoDBListConverter : CollectionConverter + internal class DynamoDBListConverter : Converter where TCollection : ICollection { - public DynamoDBListConverter() - : this(Utils.PrimitiveTypes) - { } - public DynamoDBListConverter(IEnumerable memberTypes) - : base(memberTypes) - { } - - public override bool TryTo(object value, out DynamoDBList l) + protected override bool TryTo(TCollection value, out DynamoDBList l) { - var items = value as IEnumerable; - if (items != null) + l = new DynamoDBList(); + foreach (var item in value) { - l = new DynamoDBList(); - foreach(var item in items) - { - var itemType = item.GetType(); - var entry = Conversion.ConvertToEntry(itemType, item); - l.Add(entry); - } - return true; + var entry = Conversion.ConvertToEntry(item); + l.Add(entry); } - - l = null; - return false; + return true; } - public override bool TryFrom(DynamoDBList l, Type targetType, out object result) + protected override bool TryFrom(DynamoDBList l, out TCollection result) { - var elementType = Utils.GetElementType(targetType); - var entries = l.Entries; - return EntriesToCollection(targetType, elementType, entries, out result); + var items = Conversion.ConvertFromEntries(l.Entries); + return Utils.ItemsToCollection(items, out result); } } - } diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Utils.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Utils.cs index e0e3ee1db5f3..478cb744a513 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Utils.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Utils.cs @@ -14,22 +14,18 @@ */ using System; +using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; - -using Amazon.Util.Internal; -using System.Globalization; -using System.Collections; using Amazon.DynamoDBv2.DocumentModel; +using Amazon.Util.Internal; namespace Amazon.DynamoDBv2.DataModel { -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal static class Utils { private static readonly Type[] EmptyTypes = new Type[0]; @@ -124,13 +120,42 @@ public static Type GetElementType(Type collectionType) return elementType; } +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static bool ItemsToCollection(Type targetType, IEnumerable items, out object result) { return targetType.IsArray ? - ItemsToArray(targetType, items, out result): //targetType is Array + ItemsToArray(targetType, items, out result) : //targetType is Array ItemsToIList(targetType, items, out result); //targetType is IList or has Add method. } + public static bool ItemsToCollection(IEnumerable items, out TCollection result) where TCollection : ICollection + { + if (typeof(TCollection) == typeof(T[])) + { + result = (TCollection)(object)items.ToArray(); + return true; + } + if (typeof(TCollection) == typeof(List)) + { + result = (TCollection)(object)items.ToList(); + return true; + } + if (typeof(TCollection) == typeof(HashSet)) + { + result = (TCollection)(object)new HashSet(items); + return true; + } + + result = default; + return false; + } + + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif private static bool ItemsToIList(Type targetType, IEnumerable items, out object result) { result = Utils.Instantiate(targetType); @@ -155,6 +180,9 @@ private static bool ItemsToIList(Type targetType, IEnumerable items, out return false; } +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif private static bool ItemsToArray(Type targetType, IEnumerable items, out object result) { var itemlist = items.ToList(); @@ -244,7 +272,7 @@ public static string ToLowerCamelCase(string value) private static Type[][] validArrayConstructorInputs = new Type[][] { //supports one dimension Array only - new Type[] { typeof(int) } + new Type[] { typeof(int) } }; private static Type[][] validConverterConstructorInputs = new Type[][] { @@ -252,18 +280,33 @@ public static string ToLowerCamelCase(string value) new Type[] { typeof(DynamoDBContext) } }; +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static object InstantiateConverter(Type objectType, IDynamoDBContext context) { return InstantiateHelper(objectType, validConverterConstructorInputs, new object[] { context }); } - public static object InstantiateArray(Type objectType,int length) + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif + public static object InstantiateArray(Type objectType, int length) { return InstantiateHelper(objectType, validArrayConstructorInputs, new object[] { length }); } + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static object Instantiate(Type objectType) { return InstantiateHelper(objectType, validConstructorInputs, null); } + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif private static object InstantiateHelper(Type objectType, Type[][] validConstructorInputs, object[] optionalInput = null) { if (objectType == null) @@ -287,9 +330,13 @@ private static object InstantiateHelper(Type objectType, Type[][] validConstruct throw new InvalidOperationException("Unable to find valid constructor for type " + objectType.FullName); } + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif private static IEnumerable GetConstructors(Type typeInfo, Type[][] validConstructorInputs) { - foreach(var inputTypes in validConstructorInputs) + foreach (var inputTypes in validConstructorInputs) { var constructor = typeInfo.GetConstructor(inputTypes); if (constructor != null) @@ -297,18 +344,33 @@ private static IEnumerable GetConstructors(Type typeInfo, Type[ } } +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static bool CanInstantiate(Type objectType) { return CanInstantiateHelper(objectType, validConstructorInputs); } + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static bool CanInstantiateArray(Type objectType) { return objectType.IsArray && CanInstantiateHelper(objectType, validArrayConstructorInputs); } + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static bool CanInstantiateConverter(Type objectType) { return CanInstantiateHelper(objectType, validConverterConstructorInputs); } + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif private static bool CanInstantiateHelper(Type objectType, Type[][] validConstructorInputs) { var objectTypeWrapper = objectType; @@ -358,6 +420,10 @@ public static bool IsReadWrite(MemberInfo member) throw new ArgumentOutOfRangeException("member", "Member must be FieldInfo or PropertyInfo"); } } + +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static bool ImplementsInterface(Type targetType, Type interfaceType) { if (!interfaceType.IsInterface) @@ -376,7 +442,7 @@ public static bool ImplementsInterface(Type targetType, Type interfaceType) } return false; } - + /// /// Apply a set of filters to a determine whether a member should be returned. /// In terms of DynamoDb, we want to return members that are fields or properties @@ -394,7 +460,7 @@ private static bool IsValidMemberInfo(MemberInfo member) return true; } - + /// /// Retrieves a list of members that exist in a given type. /// The function goes over all the declared members of a given type @@ -403,13 +469,16 @@ private static bool IsValidMemberInfo(MemberInfo member) /// members from the derived types will be used while ignoring same-name members /// in base types to avoid returning duplicate members. /// +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] +#endif public static List GetMembersFromType(Type type) { Dictionary members = new Dictionary(); - + Type currentType = type; while ( - currentType != null && + currentType != null && currentType != typeof(object)) { // Previous implementation used GetMembers to return the valid members for a type, but in certain class configurations @@ -420,7 +489,7 @@ public static List GetMembersFromType(Type type) .GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly) .Where(IsValidMemberInfo) .ToList(); - + foreach (var member in currentMembers) { if (!members.ContainsKey(member.Name)) @@ -428,14 +497,14 @@ public static List GetMembersFromType(Type type) members[member.Name] = member; } } - + currentType = currentType.BaseType; } - + return members.Values.ToList(); } -#endregion + #endregion } } diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Document.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Document.cs index b10dd6551eed..35ecce13e87d 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Document.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Document.cs @@ -19,7 +19,6 @@ using System.Linq; using Amazon.DynamoDBv2.Model; -using Amazon.Runtime; using Amazon.Runtime.Internal.Util; using Amazon.Util; @@ -29,9 +28,6 @@ namespace Amazon.DynamoDBv2.DocumentModel /// A collection of attribute key-value pairs that defines /// an item in DynamoDB. /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public class Document : DynamoDBEntry, IDictionary { #region Private/internal members @@ -182,7 +178,7 @@ public bool Contains(string attributeName) public Document ForceConversion(DynamoDBEntryConversion conversion) { Document newDocument = new Document(); - foreach(var kvp in this) + foreach (var kvp in this) { string name = kvp.Key; DynamoDBEntry entry = kvp.Value; @@ -688,13 +684,13 @@ private static bool TryToDynamoDBList(AttributeValue attributeValue, out DynamoD private static bool TryToDocument(AttributeValue attributeValue, out Document document) { document = null; - + if (attributeValue.IsSetM()) { document = new Document(); var items = attributeValue.M; - foreach(var kvp in items) + foreach (var kvp in items) { var name = kvp.Key; var value = kvp.Value; @@ -710,7 +706,7 @@ private static bool TryToDocument(AttributeValue attributeValue, out Document do #endregion #region Static methods - + /// /// Creates a Document from an attribute map. /// @@ -737,7 +733,7 @@ public static Document FromJson(string json) { return JsonUtils.FromJson(json); } - + /// /// Parses JSON text to produce an array of . /// @@ -905,7 +901,7 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() internal override AttributeValue ConvertToAttributeValue(AttributeConversionConfig conversionConfig) { var map = new Dictionary(StringComparer.Ordinal); - foreach(var item in currentValues) + foreach (var item in currentValues) { var key = item.Key; var entry = item.Value; @@ -956,7 +952,7 @@ public override bool Equals(object obj) if (Keys.Count != otherDocument.Keys.Count) return false; - foreach(var key in Keys) + foreach (var key in Keys) { if (!otherDocument.ContainsKey(key)) return false; @@ -978,7 +974,7 @@ public override bool Equals(object obj) public override int GetHashCode() { var hashCode = 0; - foreach(var kvp in this) + foreach (var kvp in this) { string key = kvp.Key; DynamoDBEntry entry = kvp.Value; diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBBool.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBBool.cs index 79e7e5a287d5..38b0975499e8 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBBool.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBBool.cs @@ -14,23 +14,13 @@ */ using System; -using System.Collections.Generic; -using System.Linq; - using Amazon.DynamoDBv2.Model; -using System.IO; -using Amazon.Runtime.Internal.Util; -using Amazon.Util; namespace Amazon.DynamoDBv2.DocumentModel { /// /// A DynamoDBEntry that represents a DynamoDB bool (BOOL) type. /// - -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public class DynamoDBBool : DynamoDBEntry { /// diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBEntry.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBEntry.cs index 35f3e7f83adc..f92af368cb48 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBEntry.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBEntry.cs @@ -15,11 +15,10 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.IO; using System.Linq; - using Amazon.DynamoDBv2.Model; -using System.IO; -using System.Globalization; using Amazon.Runtime.Internal.Util; using Amazon.Util; @@ -29,9 +28,6 @@ namespace Amazon.DynamoDBv2.DocumentModel /// /// Abstract class representing an arbitrary DynamoDB attribute value /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public abstract class DynamoDBEntry : ICloneable { internal class AttributeConversionConfig @@ -85,7 +81,7 @@ internal AttributeValueUpdate ConvertToAttributeUpdateValue(AttributeConversionC return attributeUpdate; } - + #endregion #region Subclass conversions @@ -577,7 +573,7 @@ public virtual String AsString() /// DynamoDBEntry representing the data public static implicit operator DynamoDBEntry(String data) { - if(data == null) + if (data == null) return new Primitive(); return new UnconvertedDynamoDBEntry(data); } @@ -1059,10 +1055,6 @@ public static explicit operator HashSet(DynamoDBEntry p) /// The entry is converted to a converted DynamoDBEntry either by the /// consuming Document or Table. /// - -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal class UnconvertedDynamoDBEntry : DynamoDBEntry { private object Value; @@ -1348,7 +1340,7 @@ public override List AsListOfByteArray() public override List AsListOfMemoryStream() { return (List)Value; - } + } #endregion diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBList.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBList.cs index 4d5c1469feed..c217b4c283a5 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBList.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DynamoDBList.cs @@ -18,7 +18,6 @@ using System.IO; using System.Linq; using Amazon.DynamoDBv2.Model; -using Amazon.Runtime; using Amazon.Runtime.Internal.Util; namespace Amazon.DynamoDBv2.DocumentModel @@ -26,23 +25,13 @@ namespace Amazon.DynamoDBv2.DocumentModel /// /// A DynamoDBEntry that represents a DynamoDB list (L) type. /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public class DynamoDBList : DynamoDBEntry { private static DynamoDBEntryConversion conversion = CreateConversion(); private static DynamoDBEntryConversion CreateConversion() { var conversion = DynamoDBEntryConversion.V2.Clone(); - var supportedMemberTypes = new Type[] - { - typeof(String), - typeof(MemoryStream), - typeof(byte[]) - }; - conversion.AddConverter(new DynamoDBListConverter(supportedMemberTypes)); - + conversion.AddConverterFactory(new DynamoDBListConverterFactory(conversion)); return conversion; } @@ -67,7 +56,7 @@ public DynamoDBList(IEnumerable entries) } /// - /// Create a DynamODBList from an IEnumerable + /// Create a DynamoDBList from an IEnumerable /// /// /// @@ -176,7 +165,7 @@ internal override AttributeValue ConvertToAttributeValue(AttributeConversionConf { entryAttributeValue = entry.ConvertToAttributeValue(conversionConfig); } - if(entryAttributeValue != null) + if (entryAttributeValue != null) { items.Add(entryAttributeValue); } @@ -219,7 +208,7 @@ public override bool Equals(object obj) if (entries.Count != otherEntries.Count) return false; - for(int i=0;i /// A DynamoDBEntry that represents a DynamoDB null (NULL) type. /// - -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public class DynamoDBNull : DynamoDBEntry { /// @@ -84,7 +73,7 @@ public override int GetHashCode() { return 0; } - + #endregion } } diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/JsonUtils.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/JsonUtils.cs index edd13c7a2e2f..d6f27698da2a 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/JsonUtils.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/JsonUtils.cs @@ -17,20 +17,13 @@ using System.Collections.Generic; using System.Globalization; using System.Text; - -using Amazon.DynamoDBv2.Model; -using Amazon.Util; using ThirdParty.Json.LitJson; -using System.IO; namespace Amazon.DynamoDBv2.DocumentModel { /// /// Utility methods to handle conversion from/to JSON /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif internal static class JsonUtils { /// @@ -61,9 +54,9 @@ public static IEnumerable FromJsonArray(string jsonText) var json = JsonMapper.ToObject(jsonText); if (!json.IsArray) throw new InvalidOperationException("Expected array at JSON root."); - + var array = new List(); - for(int i=0;i(StringComparer.Ordinal); // Convert, but don't alter the original yet - foreach(var attributeName in attributeNames) + foreach (var attributeName in attributeNames) { DynamoDBEntry entry; // If an attribute is not present, do nothing @@ -128,7 +121,7 @@ public static void DecodeBase64Attributes(Document document, params string[] att } // Update document with decoded attribute values - foreach(var kvp in decodedValues) + foreach (var kvp in decodedValues) { var attributeName = kvp.Key; var decodedEntry = kvp.Value; @@ -164,7 +157,7 @@ private static bool TryDecodeBase64(DynamoDBEntry entry, out DynamoDBEntry decod if (primitiveList != null && primitiveList.Type == DynamoDBEntryType.String) { var decodedList = new PrimitiveList(DynamoDBEntryType.Binary); - foreach(var item in primitiveList.Entries) + foreach (var item in primitiveList.Entries) { // Attempt to decode DynamoDBEntry decodedItem; @@ -189,7 +182,7 @@ private static bool TryDecodeBase64(DynamoDBEntry entry, out DynamoDBEntry decod if (dynamoDBList != null) { var decodedList = new DynamoDBList(); - foreach(var item in dynamoDBList.Entries) + foreach (var item in dynamoDBList.Entries) { DynamoDBEntry decodedItem; if (!TryDecodeBase64(item, out decodedItem)) @@ -248,7 +241,7 @@ private static DynamoDBEntry ToEntry(JsonData data, DynamoDBEntryConversion conv if (data.IsArray) { var list = new DynamoDBList(); - for(int i=0;i /// Enumerator describing type of DynamoDB data in a Primitive or PrimitiveList /// - public enum DynamoDBEntryType { + public enum DynamoDBEntryType + { /// /// DynamoDB String type. /// - String, + String, /// /// DynamoDB Numeric type. /// - Numeric, + Numeric, /// /// DynamoDB Binary type. /// - Binary + Binary } /// /// A DynamoDBEntry that represents a scalar DynamoDB type /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public class Primitive : DynamoDBEntry, IEquatable { #region Private members diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/PrimitiveList.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/PrimitiveList.cs index 5f117d82a6dd..21b6f2a59a89 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/PrimitiveList.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/PrimitiveList.cs @@ -15,21 +15,16 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; - using Amazon.DynamoDBv2.Model; -using System.IO; using Amazon.Runtime.Internal.Util; -using Amazon.Util; namespace Amazon.DynamoDBv2.DocumentModel { /// /// A DynamoDBEntry that represents a primitive list DynamoDB type /// -#if NET8_0_OR_GREATER - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)] -#endif public class PrimitiveList : DynamoDBEntry, IEquatable { private static DynamoDBEntryConversion V1Conversion = DynamoDBEntryConversion.V1; @@ -172,7 +167,7 @@ internal List GetSortedEntries() } private class PrimitiveComparer : IComparer - { + { public int Compare(Primitive x, Primitive y) { if (x.Type != y.Type) @@ -197,12 +192,12 @@ public int Compare(Primitive x, Primitive y) int byteCompare = xb.CompareTo(yb); if (byteCompare != 0) return byteCompare; - } + } return 0; - } + } else - { + { throw new InvalidOperationException("Unknown type of Primitive: " + x.Type); } } @@ -494,7 +489,7 @@ public override int GetHashCode() { var typeHashCode = this.Type.GetHashCode(); var entriesHashCode = 0; - foreach(var entry in this.Entries) + foreach (var entry in this.Entries) { // Hash entries in such a way that order doesn't matter entriesHashCode = entriesHashCode ^ entry.GetHashCode(); @@ -534,7 +529,7 @@ public override bool Equals(object obj) return true; } - + #endregion #region IEquatable Members diff --git a/sdk/test/Services/DynamoDBv2/AWSSDK.DynamoDBv2.AotTest/AWSSDK.DynamoDBv2.AotTest.csproj b/sdk/test/Services/DynamoDBv2/AWSSDK.DynamoDBv2.AotTest/AWSSDK.DynamoDBv2.AotTest.csproj new file mode 100644 index 000000000000..d4c5c28a353b --- /dev/null +++ b/sdk/test/Services/DynamoDBv2/AWSSDK.DynamoDBv2.AotTest/AWSSDK.DynamoDBv2.AotTest.csproj @@ -0,0 +1,23 @@ + + + + Exe + net8.0 + enable + enable + true + false + true + + + + + + + + + + + + + diff --git a/sdk/test/Services/DynamoDBv2/AWSSDK.DynamoDBv2.AotTest/Program.cs b/sdk/test/Services/DynamoDBv2/AWSSDK.DynamoDBv2.AotTest/Program.cs new file mode 100644 index 000000000000..ffc562a24b75 --- /dev/null +++ b/sdk/test/Services/DynamoDBv2/AWSSDK.DynamoDBv2.AotTest/Program.cs @@ -0,0 +1,75 @@ +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DataModel; +using Amazon.DynamoDBv2.Model; +using Amazon.Runtime; + +// The code below was tested using DynamoDbLocal: +// https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html + +var config = new AmazonDynamoDBConfig { ServiceURL = $"http://localhost:8000" }; +var credentials = new BasicAWSCredentials("access", "secret"); +var dynamoDbClient = new AmazonDynamoDBClient(credentials, config); +var context = new DynamoDBContext(dynamoDbClient); + +var tables = await dynamoDbClient.ListTablesAsync(); +if (!tables.TableNames.Contains("TestTable")) +{ + await dynamoDbClient.CreateTableAsync(new CreateTableRequest + { + TableName = "TestTable", + AttributeDefinitions = + [ + new AttributeDefinition + { + AttributeName = "Id", + AttributeType = ScalarAttributeType.S + } + ], + KeySchema = + [ + new KeySchemaElement + { + AttributeName = "Id", + KeyType = KeyType.HASH + } + ], + ProvisionedThroughput = new ProvisionedThroughput + { + ReadCapacityUnits = 1, + WriteCapacityUnits = 1 + } + }); + + Console.WriteLine("Table created"); +} + +await context.SaveAsync(new TestType +{ + Id = "1", + Name = "Test", + Values = ["Value1", "Value2"], + SubType = new() { SubName = "Subname" } +}); +Console.WriteLine("Item saved."); + +var item = await context.LoadAsync("1"); +Console.WriteLine($"Item loaded: {item}"); +Console.WriteLine($"Values: {string.Join(",", item.Values)}"); + +[DynamoDBTable("TestTable")] +record class TestType +{ + [DynamoDBHashKey] + public string Id { get; set; } + + public string Name { get; set; } + + public HashSet Values { get; set; } + + public SubType SubType { get; set; } +} + +record class SubType +{ + public string SubName { get; set; } +} \ No newline at end of file diff --git a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBEntryConversionTests.cs b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBEntryConversionTests.cs index 82b9af68e070..b56fd0ed60a0 100644 --- a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBEntryConversionTests.cs +++ b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBEntryConversionTests.cs @@ -1,71 +1,280 @@ -using Amazon.DynamoDBv2; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Reflection; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DocumentModel; +using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AWSSDK_DotNet35.UnitTests +namespace AWSSDK.UnitTests.DynamoDBv2.Net45.Custom { [TestClass] public class DynamoDBEntryConversionTests { - [TestMethod] - public void ValidateAllConvertersAreRegisteredForConversionV1() + [DataTestMethod] + [DynamicData(nameof(V1PrimitiveData))] + public void V1_ConvertToEntry_Primitives(object value, DynamoDBEntry expectedPrimitive) { - AssertAllConvertersAreRegistered(DynamoDBEntryConversion.V1, "ConverterV1"); + var actualPrimitive = DynamoDBEntryConversion.V1.ConvertToEntry(value.GetType(), value); + Assert.AreEqual(expectedPrimitive, actualPrimitive); } - [TestMethod] - public void ValidateAllConvertersAreRegisteredForConversionV2() + [DataTestMethod] + [DynamicData(nameof(V1PrimitiveData))] + public void V1_ConvertFromEntry_Primitives(object expectedValue, DynamoDBEntry primitive) + { + var actualValue = DynamoDBEntryConversion.V1.ConvertFromEntry(expectedValue.GetType(), primitive); + Assert.AreEqual(expectedValue, actualValue); + } + + [DataTestMethod] + [DynamicData(nameof(V2PrimitiveData))] + public void V2_ConvertToEntry_Primitives(object value, DynamoDBEntry expectedPrimitive) + { + var actualPrimitive = DynamoDBEntryConversion.V2.ConvertToEntry(value.GetType(), value); + Assert.AreEqual(expectedPrimitive, actualPrimitive); + } + + [DataTestMethod] + [DynamicData(nameof(V2PrimitiveData))] + public void V2_ConvertFromEntry_Primitives(object expectedValue, DynamoDBEntry primitive) { - AssertAllConvertersAreRegistered(DynamoDBEntryConversion.V2, "ConverterV2"); + var actualValue = DynamoDBEntryConversion.V2.ConvertFromEntry(expectedValue.GetType(), primitive); + Assert.AreEqual(expectedValue, actualValue); } - private void AssertAllConvertersAreRegistered(DynamoDBEntryConversion conversion, string suffix) + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertToEntry_MemoryStream(DynamoDBEntryConversion conversion) { - var converters = GetConverters(suffix); + var memoryStream = new MemoryStream(ByteArray); + var entry = conversion.ConvertToEntry(memoryStream); + Assert.AreEqual(new Primitive(ByteArray), entry); + } + + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertFromEntry_MemoryStream(DynamoDBEntryConversion conversion) + { + var entry = new Primitive(ByteArray); + var memoryStream = conversion.ConvertFromEntry(entry); + CollectionAssert.AreEqual(ByteArray, memoryStream.ToArray()); + } - var tryGetConverterInfo = conversion.GetType().GetMethod("TryGetConverter", BindingFlags.NonPublic | BindingFlags.Instance); + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertToEntry_DateTime(DynamoDBEntryConversion conversion) + { + var dateTime = new DateTime(2024, 07, 03, 01, 31, 47, DateTimeKind.Utc); + var entry = conversion.ConvertToEntry(dateTime); + Assert.AreEqual(new Primitive("2024-07-03T01:31:47.000Z", false), entry); + } + + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertFromEntry_DateTime(DynamoDBEntryConversion conversion) + { + // DynamoDBEntryConversion returns local DateTime by default: + // https://github.com/aws/aws-sdk-net/issues/1450 + var entry = new Primitive("2024-07-03T01:31:47.000Z", false); + var actualDateTime = conversion.ConvertFromEntry(entry); + var utcDateTime = actualDateTime.ToUniversalTime(); + var expectedDateTime = new DateTime(2024, 07, 03, 01, 31, 47, DateTimeKind.Utc); + Assert.AreEqual(expectedDateTime, utcDateTime); + } - foreach (var converter in converters) + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertToEntry_Dictionary(DynamoDBEntryConversion conversion) + { + var dictionary = new Dictionary { - var getTargetTypeInfo = converter.GetType().GetMethod("GetTargetTypes"); - IEnumerable targetTypes = (IEnumerable)getTargetTypeInfo.Invoke(converter, new object[0]); - foreach (var type in targetTypes) + ["key1"] = "str", + ["key2"] = 123, + ["key3"] = new Dictionary { - var tryGetConverterParams = new object[] { type, null }; - tryGetConverterInfo.Invoke(conversion, tryGetConverterParams); - var registeredConverter = tryGetConverterParams[1]; + ["innerKey"] = "innerStr" + } + }; - Assert.IsNotNull(registeredConverter); - Assert.AreEqual(converter.GetType(), registeredConverter.GetType()); + var document = conversion.ConvertToEntry(dictionary).AsDocument(); + + var expectedDocument = new Document + { + ["key1"] = new Primitive("str"), + ["key2"] = new Primitive("123", true), + ["key3"] = new Document + { + ["innerKey"] = new Primitive("innerStr") } - } + }; + Assert.AreEqual(expectedDocument, document); + } + + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertFromEntry_DynamoDbList_ToArray(DynamoDBEntryConversion conversion) + { + var dynamoDbList = new DynamoDBList(new DynamoDBEntry[] + { + new Primitive("A"), new Primitive("B"), new Primitive("C") + }); + var convertedArray = conversion.ConvertFromEntry(dynamoDbList); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, convertedArray); } - private IEnumerable GetConverters(string suffix) + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertFromEntry_DynamoDbList_ToList(DynamoDBEntryConversion conversion) { - const string converterTypeName = "Amazon.DynamoDBv2.Converter"; - var assembly = typeof(DynamoDBEntryConversion).Assembly; + var dynamoDbList = new DynamoDBList(new DynamoDBEntry[] + { + new Primitive("1", true), new Primitive("2", true), new Primitive("3", true) + }); + var convertedList = conversion.ConvertFromEntry>(dynamoDbList); + CollectionAssert.AreEqual(new[] { 1, 2, 3 }, convertedList); + } - var allTypes = assembly.GetTypes(); - var typedConverterType = allTypes.FirstOrDefault(x => string.Equals(converterTypeName, x.FullName)); + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertFromEntry_PrimitiveList_ToArray(DynamoDBEntryConversion conversion) + { + var primitiveList = new PrimitiveList(DynamoDBEntryType.String) + { + Entries = { "A", "B", "C" } + }; + var convertedArray = conversion.ConvertFromEntry(primitiveList); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, convertedArray); + } + + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertFromEntry_PrimitiveList_ToList(DynamoDBEntryConversion conversion) + { + var primitiveList = new PrimitiveList(DynamoDBEntryType.Numeric) + { + Entries = { 1, 2, 3 } + }; + var convertedList = conversion.ConvertFromEntry>(primitiveList); + CollectionAssert.AreEqual(new[] { 1, 2, 3 }, convertedList); + } - foreach (var type in allTypes) + [DataTestMethod] + [DynamicData((nameof(DynamoDBEntryConversions)))] + public void ConvertFromEntry_PrimitiveList_ToHashSet(DynamoDBEntryConversion conversion) + { + var primitiveList = new PrimitiveList(DynamoDBEntryType.Numeric) { - if (type.IsAbstract) - continue; + Entries = { 0, 1, 2 } + }; + var convertedHashSet = conversion.ConvertFromEntry>(primitiveList); + Assert.IsTrue(convertedHashSet.SetEquals(new long[] { 0, 1, 2 })); + } + + [TestMethod] + public void V1_ConvertToEntry_Array() + { + var array = new[] { "A", "B", "C" }; + var entry = DynamoDBEntryConversion.V1.ConvertToEntry(array); + var expectedList = new PrimitiveList { Entries = { "A", "B", "C" } }; + Assert.AreEqual(expectedList, entry); + } + + [TestMethod] + public void V1_ConvertToEntry_List() + { + var list = new List { 3, 2, 1 }; + var entry = DynamoDBEntryConversion.V1.ConvertToEntry(list); + var expectedList = new PrimitiveList(DynamoDBEntryType.Numeric) { Entries = { 3, 2, 1 } }; + Assert.AreEqual(expectedList, entry); + } - if (!type.Name.EndsWith(suffix, StringComparison.Ordinal)) - continue; + [TestMethod] + public void V1_ConvertToEntry_HashSet() + { + var hashSet = new HashSet { 0, 1, 2 }; + var entry = DynamoDBEntryConversion.V1.ConvertToEntry(hashSet); + var expectedList = new PrimitiveList(DynamoDBEntryType.Numeric) { Entries = { 0, 1, 2 } }; + Assert.AreEqual(expectedList, entry); + } + + [TestMethod] + public void V2_ConvertToEntry_Array() + { + var array = new[] { "A", "B", "C" }; + var entry = DynamoDBEntryConversion.V2.ConvertToEntry(array); + var expectedList = new DynamoDBList(new DynamoDBEntry[] + { + new Primitive("A"), new Primitive("B"), new Primitive("C") + }); + Assert.AreEqual(expectedList, entry); + } - if (!typedConverterType.IsAssignableFrom(type)) - continue; + [TestMethod] + public void V2_ConvertToEntry_List() + { + var list = new List { 3, 2, 1 }; + var entry = DynamoDBEntryConversion.V2.ConvertToEntry(list); + var expectedList = new DynamoDBList(new DynamoDBEntry[] + { + new Primitive("3", true), new Primitive("2", true), new Primitive("1", true) + }); + Assert.AreEqual(expectedList, entry); + } - var constructor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new Type[0], null); - yield return constructor.Invoke(new object[0]); - } + [TestMethod] + public void V2_ConvertToEntry_HashSet() + { + var hashSet = new HashSet { 0, 1, 2 }; + var entry = DynamoDBEntryConversion.V2.ConvertToEntry(hashSet); + var expectedList = new PrimitiveList(DynamoDBEntryType.Numeric) + { + Entries = { new Primitive("0", true), new Primitive("1", true), new Primitive("2", true) } + }; + Assert.AreEqual(expectedList, entry); } + + private static byte[] ByteArray = new byte[] { 1, 2, 3, 4, 5 }; + + private static IEnumerable DynamoDBEntryConversions { get; } = new object[][] + { + new object[] { DynamoDBEntryConversion.V1 }, + new object[] { DynamoDBEntryConversion.V2 }, + }; + + private static IEnumerable CommonPrimitiveData { get; } = new object[][] + { + new object[] { (short)-1, new Primitive("-1", true) }, + new object[] { (ushort)1, new Primitive("1", true) }, + new object[] { (int)-2, new Primitive("-2", true) }, + new object[] { (uint)2, new Primitive("2", true) }, + new object[] { (long)-3, new Primitive("-3", true) }, + new object[] { (ulong)3, new Primitive("3", true) }, + new object[] { (byte)4, new Primitive("4", true) }, + new object[] { (sbyte)5, new Primitive("5", true) }, + new object[] { (float)1.23, new Primitive("1.23", true) }, + new object[] { (double)2.34, new Primitive("2.34", true) }, + new object[] { (decimal)3.45, new Primitive("3.45", true) }, + new object[] { (char)'c', new Primitive("c", false) }, + new object[] { "stringValue", new Primitive("stringValue", false) }, + new object[] { new Guid("6a34ff83-9d2b-4893-950f-1da35414f9b0"), new Primitive("6a34ff83-9d2b-4893-950f-1da35414f9b0", false) }, + new object[] { TestEnum.A, new Primitive("0", true) }, + new object[] { TestEnum.B, new Primitive("1", true) }, + new object[] { ByteArray, new Primitive(ByteArray) } + }; + + public static IEnumerable V1PrimitiveData { get; } = CommonPrimitiveData.Concat(new object[][] + { + new object[] { true, new Primitive("1", true) }, + new object[] { false, new Primitive("0", true) } + }); + + public static IEnumerable V2PrimitiveData { get; } = CommonPrimitiveData.Concat(new object[][] + { + new object[] { true, new DynamoDBBool(true) }, + new object[] { false, new DynamoDBBool(false) } + }); + + private enum TestEnum { A, B, C } } } \ No newline at end of file