-
Notifications
You must be signed in to change notification settings - Fork 848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DynamoDBEntryConversion AOT compatibility #3300
Open
Dreamescaper
wants to merge
10
commits into
aws:main-staging
Choose a base branch
from
Dreamescaper:dynamodb_aot
base: main-staging
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
b09b580
Do not construct types upfront in Converters
Dreamescaper 08cc134
POC
Dreamescaper 7072a05
Merge fixes
Dreamescaper 39d798e
Add UnconditionalSuppressMessage for MakeGenericType
Dreamescaper 0482031
Merge main
Dreamescaper b8d7219
Rename to DefaultConverterFactory
Dreamescaper db0e727
Remove CodeAnalysisAttributes
Dreamescaper 072e208
AotTest cleanup
Dreamescaper 60d91bb
Add tests for DynamoDBEntryConversion for Primitives
Dreamescaper b512f50
Add tests
Dreamescaper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
/// </summary> | ||
#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<DynamoDBEntry> ConvertToEntries<T>(IEnumerable<T> values) | |
//foreach (var value in values) | ||
// yield return ConvertToEntry(value); | ||
} | ||
|
||
internal IEnumerable<object> ConvertFromEntries(Type elementType, IEnumerable<DynamoDBEntry> entries) | ||
{ | ||
if (entries == null) throw new ArgumentNullException("entries"); | ||
|
@@ -368,6 +366,14 @@ internal IEnumerable<object> ConvertFromEntries(Type elementType, IEnumerable<Dy | |
yield return ConvertFromEntry(elementType, entry); | ||
} | ||
|
||
internal IEnumerable<T> ConvertFromEntries<T>(IEnumerable<DynamoDBEntry> entries) | ||
{ | ||
if (entries == null) throw new ArgumentNullException("entries"); | ||
|
||
foreach (var entry in entries) | ||
yield return ConvertFromEntry<T>(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 WellKnownTypesConverterFactoryV1(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 WellKnownTypesConverterFactoryV2(this)); | ||
AddConverterFactory(new CollectionConverterFactoryV2(this)); | ||
} | ||
|
||
// Converts items to Primitives. | ||
|
@@ -468,14 +439,30 @@ private IEnumerable<Primitive> ToPrimitives(IEnumerable items, Type elementType) | |
#endregion | ||
} | ||
|
||
internal abstract class Converter | ||
internal abstract class ConverterFactory | ||
{ | ||
/// <summary> | ||
/// Returns all types for which it can be used. | ||
/// </summary> | ||
/// <returns></returns> | ||
public abstract IEnumerable<Type> 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 | ||
{ | ||
/// <summary> | ||
/// 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<T> : Converter | ||
{ | ||
public override IEnumerable<Type> GetTargetTypes() | ||
{ | ||
var type = typeof(T); | ||
yield return type; | ||
|
||
if (type.IsValueType) | ||
{ | ||
//yield return typeof(Nullable<T>); | ||
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<Type, Converter> Cache = new Dictionary<Type, Converter>(); | ||
private readonly ConcurrentDictionary<Type, Converter> Cache = new ConcurrentDictionary<Type, Converter>(); | ||
private readonly List<ConverterFactory> Factories = new List<ConverterFactory>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously all the converters were created upfront, but now they are created lazily, when requested. Therefore, ConcurrentDictionary is used for cache, as it is possible that converter is requested from multiple threads at the same time. |
||
|
||
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a separate
targetType
parameter for a generic Converter kindof defeats the purpose, and not really AOT-friendly, therefore I have removed it.The only Converter to actually use it is
EnumConverter: Converter<Enum>
. But it doesn't make any value to have anEnum
generic argument, so I've updated it to inherit from non-genericConverter
.(Not a public API, so no breaking changes here)