Skip to content
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

DynamoDB AOT fails for type and collection properties #3646

Open
dscpinheiro opened this issue Feb 10, 2025 · 8 comments
Open

DynamoDB AOT fails for type and collection properties #3646

dscpinheiro opened this issue Feb 10, 2025 · 8 comments
Labels
aot Ahead of Time bug This issue is a bug. p2 This is a standard priority issue queued v4

Comments

@dscpinheiro
Copy link
Contributor

@normj
I've tested AOT integration with 4.0.0-preview.6.
There were no AOT warnings, however apparently, it doesn't support type properties and collection properties.

  1. Nested Types:
var nestedTypeId = Random.Shared.Next().ToString();
await context.SaveAsync(new TypeWithNestedTypeProperty
{
    Id = nestedTypeId,
    Name = "Test",
    SubType = new SubType() { SubName = "Subname" }
});
Console.WriteLine("Item saved.");

var nestedItem = await context.LoadAsync<TypeWithNestedTypeProperty>(nestedTypeId);
Console.WriteLine($"Item loaded: {nestedItem}");
Console.WriteLine($"SubType.SubName: {nestedItem.SubType.SubName}");

[DynamoDBTable("TestTable")]
class TypeWithNestedTypeProperty
{
    [DynamoDBHashKey]
    public string Id { get; set; }
    public string Name { get; set; }
    public SubType SubType { get; set; }
}

class SubType
{
    public string SubName { get; set; }
}

Result:

Unhandled Exception: System.InvalidOperationException: Type SubType is unsupported, it cannot be instantiated
   at Amazon.DynamoDBv2.DataModel.StorageConfig..ctor(Type) + 0x14f
   at Amazon.DynamoDBv2.DataModel.ItemStorageConfig..ctor(Type) + 0x12
   at Amazon.DynamoDBv2.DataModel.ItemStorageConfigCache.CreateStorageConfig(Type, String, DynamoDBFlatConfig) + 0x43
   at Amazon.DynamoDBv2.DataModel.ItemStorageConfigCache.GetConfig(Type, DynamoDBFlatConfig, Boolean) + 0x144
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.SerializeToDocument(Object, Type, DynamoDBFlatConfig) + 0x2c
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.ToDynamoDBEntry(SimplePropertyStorage, Object, DynamoDBFlatConfig, Boolean) + 0x125
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.PopulateItemStorage(Object, ItemStorage, DynamoDBFlatConfig, Boolean, Boolean) + 0xe6
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.ObjectToItemStorage(Object, Type, Boolean, DynamoDBFlatConfig) + 0x7e
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.<SaveHelperAsync>d__48.MoveNext() + 0x5d
  1. Collection property:
var listTypeId = Random.Shared.Next().ToString();
await context.SaveAsync(new TypeWithCollectionProperty
{
    Id = listTypeId,
    Name = "Test",
    Items = new List<string> { "1", "2", "3" }
});
Console.WriteLine("Item saved.");

var collectionItem = await context.LoadAsync<TypeWithCollectionProperty>(listTypeId);
Console.WriteLine($"Item loaded: {collectionItem}");
Console.WriteLine($"Items: {string.Join(",", collectionItem.Items)}");

Result:

Unhandled Exception: System.InvalidOperationException: Cannot instantiate type System.Collections.Generic.List`1[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
   at Amazon.DynamoDBv2.DataModel.Utils.InstantiateHelper(Type, Type[][], Object[]) + 0x119
   at Amazon.DynamoDBv2.DataModel.Utils.ItemsToIList(Type, IEnumerable`1, Object&) + 0x42
   at Amazon.DynamoDBv2.Converter.TryFromEntry(DynamoDBEntry, Type, Object&) + 0x79
   at Amazon.DynamoDBv2.Converter.FromEntry(DynamoDBEntry, Type) + 0x39
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.FromDynamoDBEntry(SimplePropertyStorage, DynamoDBEntry, DynamoDBFlatConfig) + 0x7f
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.PopulateInstance(ItemStorage, Object, DynamoDBFlatConfig) + 0xc4
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.DocumentToObject(Type, ItemStorage, DynamoDBFlatConfig) + 0x47
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.DocumentToObject[T](ItemStorage, DynamoDBFlatConfig) + 0x32
   at Amazon.DynamoDBv2.DataModel.DynamoDBContext.<LoadHelperAsync>d__57`1.MoveNext() + 0x1c4

Attaching sample project:
DdbAORRepro.zip

Originally posted by @Dreamescaper in #3475

@Dreamescaper
Copy link
Contributor

Personally, I don't think it is possible to fully fix without a Source Generator.
(It is possible to workaround for some specific types - like specifically for List<T>. But not for any arbitrary type.)

But it probably should be documented somehow.

@ashishdhingra ashishdhingra added the p2 This is a standard priority issue label Feb 10, 2025
@normj
Copy link
Member

normj commented Feb 10, 2025

Good catch. You are probably right that a source generator would be the ideal solution but not something we have bandwidth for, at least in the near future. You can work around the issue by adding the DynamicDependency attribute for the nested type. I think what I will do in the short time is catch the System.InvalidOperationException: Type <type> is unsupported, it cannot be instantiated and rethrow with a message that hints at using DynamicDependency when running in Native AOT mode. It will also call this limitation in our documentation as we get that ready for V4. I did update the V4 tracker issue about this limitation. #3362

[DynamoDBTable("TestTable")]
class TypeWithNestedTypeProperty
{
    [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(SubType))]
    public TypeWithNestedTypeProperty()
    {

    }

    [DynamoDBHashKey]
    public string Id { get; set; }
    public string Name { get; set; }

    public SubType SubType { get; set; }
}

class SubType
{
    public string SubName { get; set; }
}

@Dreamescaper
Copy link
Contributor

Regarding specifically collection properties. DynamoDBContext supports very limited set of collection types - T[], List<T>, HashSet<T>.
Therefore, it is possible to create those specific types mostly without reflection, and to be AOT compatible.
(see https://github.com/aws/aws-sdk-net/pull/3300/files#diff-e9d2d59bba99ce62d79158551bd80d3d71d18a3ba7f60059bdd915fdae5abbbf )

Not sure if anything could be done for nested types...

@MichalStrehovsky
Copy link

Why is there no AOT/trim warning generated? Does the library have an invalid warning suppression or did the tooling miss something?

@Dreamescaper
Copy link
Contributor

Dreamescaper commented Feb 14, 2025

@MichalStrehovsky
Probably due to warning suppressions.

Anyway, I've created a discussion at dotnet/runtime, which would potentially allow supporting nested types without a source generator.
Maybe it gets some traction.
dotnet/runtime#112356

@normj
Copy link
Member

normj commented Feb 24, 2025

@MichalStrehovsky When the type the user wants to save into DynamoDB is passed into our library I'm using the attribute DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All) knowing the SDK will do need to access all of the properties and fields and at least the 0 parameter constructor. I had assume by using DynamicallyAccessedMemberTypes.All that should include all the metadata on the types referenced by parent type. I figured DynamicallyAccessedMemberTypes.All was basically saying I need this whole type to not be trimmed because in our case we need to do all the reflection on the type. Am I misunderstanding the meaning of DynamicallyAccessedMemberTypes.All?

@normj
Copy link
Member

normj commented Feb 25, 2025

Preview 8 of V4 is out which updates the error message to give user's directions on how to address the issue.

@Dreamescaper
Copy link
Contributor

I had assume by using DynamicallyAccessedMemberTypes.All that should include all the metadata on the types referenced by parent type.

No, it only includes all the members of the current type. That doesn't include properties' types.
Hence I created this proposal to allow it to work recursively

dotnet/runtime#112356

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aot Ahead of Time bug This issue is a bug. p2 This is a standard priority issue queued v4
Projects
None yet
Development

No branches or pull requests

5 participants