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

Amazon DynamoDBContext - NET 7 AOT Incompatibilities #2542

Open
rac146 opened this issue Feb 15, 2023 · 13 comments
Open

Amazon DynamoDBContext - NET 7 AOT Incompatibilities #2542

rac146 opened this issue Feb 15, 2023 · 13 comments
Labels
aot Ahead of Time bug This issue is a bug. dynamodb p2 This is a standard priority issue queued

Comments

@rac146
Copy link

rac146 commented Feb 15, 2023

Describe the bug

Was running some NET7 AOT experiments with DynamoDBContext and was receiving runtime errors after doing an AOT compilation:

System.NotSupportedException: 'System.Collections.Generic.HashSet`1[System.Boolean]' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility
   at System.Reflection.Runtime.General.TypeUnifier.WithVerifiedTypeHandle(RuntimeConstructedGenericTypeInfo, RuntimeTypeInfo[]) + 0x88
   at Amazon.DynamoDBv2.CollectionConverter.<GetTargetTypes>d__1.MoveNext() + 0x1d4
   at Amazon.DynamoDBv2.ConverterCache.AddConverter(Converter converter, DynamoDBEntryConversion conversion) + 0x9b
   at Amazon.DynamoDBv2.DynamoDBEntryConversion.AddConverter(Type type) + 0x41
   at Amazon.DynamoDBv2.DynamoDBEntryConversion.AddConverters(String suffix) + 0x184
   at Amazon.DynamoDBv2.DynamoDBEntryConversion..ctor(ConversionSchema schema, Boolean isImmutable) + 0x6e
   at Amazon.DynamoDBv2.DynamoDBEntryConversion..cctor() + 0x25
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xc6

Expected Behavior

For DynamoDB to be AOT NET 7 supported

Current Behavior

Errors out after dotnet publish in NET7 AOT mode

Reproduction Steps

Attempt to run any DynamoDB queries that use DynamoDBContext with latest version of AWS SDK

Possible Solution

Decided to hack into the SDK to find the culprit. Discovered .MakeGenericType is throwing the runtime errors.
Switched these 4 lines for experimentation purposes:

In DynamoDBEntryConversion.cs:
Line 651: var nullableType = typeof(Nullable<>).MakeGenericType(type)
Potential Fix?: yield return typeof(Nullable<>);

In SchemaV1.cs:

Line 379: yield return pt.MakeArrayType();
Fix?: yield return typeof(Array);
Line 382: yield return listType.MakeGenericType(pt);
Fix?: yield return typeof(List<>);
Line 384: yield return setType.MakeGenericType(pt);
Fix?: yield return typeof(HashSet<>);

This allowed my test application to run and work correctly. Is this is right long-term fix to the problem? I don't know, but just wanted to let you know!

Additional Information/Context

With AWS starting to push AOT further, it would be great to start working through the bugs that prevent an AOT build from working. I believe this fits as a bug opposed to a feature request, especially with the push towards AOT compatible lambdas!

AWS .NET SDK and/or Package version used

AWS SDK DynamoDBv2 3.7.101.44

Targeted .NET Platform

.NET 7

Operating System and version

Windows 10

@rac146 rac146 added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Feb 15, 2023
@ashishdhingra
Copy link
Contributor

Hi @rac146,

Good afternoon.

Thanks for opening the issue. Just wanted to check if you referred the blog post Building serverless .NET applications on AWS Lambda using .NET 7 for the Native AOT support. As mentioned, currently, the AWSSDK.Core library used by all the .NET AWS SDKs must also be excluded from trimming. I guess this is also the case with AWSSDK.* assemblies. Could you please try adding AWSSDK.DynamoDBv2 in rd.xml and see if it works. We have the internal backlog item to support trimming for AWS SDK for .NET assemblies, however, do not have the concrete timeline by which it would be supported.

Thanks,
Ashish

@ashishdhingra ashishdhingra added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed needs-triage This issue or PR still needs to be triaged. labels Feb 15, 2023
@rac146
Copy link
Author

rac146 commented Feb 16, 2023

Hey Ashish,

Even excluding AWSSDK.DynamoDBv2 from trimming, the error still occurs. This error will present itself with or without trimming - this is flagged as an AOT analysis warning, not a trimming warning when running dotnet publish.

@rac146
Copy link
Author

rac146 commented Feb 16, 2023

Here are the detailed errors when running dotnet publish with <TrimmerSingleWarn>false</TrimmerSingleWarn>

D:\JenkinsWorkspaces\trebuchet-stage-release\AWSDotNetPublic\sdk\src\Services\DynamoDBv2\Custom\Conversion\DynamoDBEntryConversion.cs(652): AOT analysis warning IL3050: Amazon.DynamoDBv2.Converter`1.d__0.MoveNext(): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

D:\JenkinsWorkspaces\trebuchet-stage-release\AWSDotNetPublic\sdk\src\Services\DynamoDBv2\Custom\Conversion\SchemaV1.cs(379): AOT analysis warning IL3050: Amazon.DynamoDBv2.CollectionConverter.d__1.MoveNext(): Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.

D:\JenkinsWorkspaces\trebuchet-stage-release\AWSDotNetPublic\sdk\src\Services\DynamoDBv2\Custom\Conversion\SchemaV1.cs(382): AOT analysis warning IL3050: Amazon.DynamoDBv2.CollectionConverter.d__1.MoveNext(): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

D:\JenkinsWorkspaces\trebuchet-stage-release\AWSDotNetPublic\sdk\src\Services\DynamoDBv2\Custom\Conversion\SchemaV1.cs(384): AOT analysis warning IL3050: Amazon.DynamoDBv2.CollectionConverter.d__1.MoveNext(): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Feb 17, 2023
@eddiemcs3 eddiemcs3 added p2 This is a standard priority issue aot Ahead of Time and removed needs-review labels Feb 24, 2023
@jesmiatka
Copy link

I've got the current version of the AWSSDK working for now with DynamoDB if I add the following to the rd.xml

<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
  <Application>
    <Assembly Name="AWSSDK.Core" Dynamic="Required All"></Assembly>
    <Assembly Name="AWSSDK.DynamoDBv2" Dynamic="Required All"></Assembly>
    <Assembly Name="System.Collections">
      <Type Name="System.Collections.Generic.HashSet`1[[System.Boolean,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Byte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Byte[],System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Char,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.DateTime,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Decimal,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Double,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Guid,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Int16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Int32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Int64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.UInt16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.UInt32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.UInt64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.SByte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Single,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.String,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Boolean,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Byte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Byte[],System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Char,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.DateTime,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Decimal,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Double,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Guid,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Int16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Int32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Int64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.UInt16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.UInt32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.UInt64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.SByte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Single,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.String,System.Runtime]]" Dynamic="Required All" />
    </Assembly>
  </Application>
</Directives>

Hope this helps other for now.

@MrZoidberg
Copy link

Adding to @jesmiatka. I needed these lines additionally to make it work:

<Type Name="System.Nullable`1[System.Byte]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.SByte]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.UInt16]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Int32]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.UInt32]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Int16]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Int64]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.UInt64]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Single]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Double]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Decimal]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Char]" Dynamic="Required All"/>'
<Type Name="System.Nullable`1[System.DateTime]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Guid]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Enum]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.Boolean]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.IO.MemoryStream]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.String]" Dynamic="Required All"/>

@smartshader
Copy link

@MrZoidberg In which assembly is System.Nullable? Can you share the whole Assembly entry in rd.xml?

@branog68
Copy link

In System.Runtime. Here the whole rd.xml:

<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
  <Application>
	<Assembly Name="AWSSDK.Core" Dynamic="Required All"></Assembly>
	<Assembly Name="AWSSDK.DynamoDBv2" Dynamic="Required All"></Assembly>
	<Assembly Name="System.Collections">
	  <Type Name="System.Collections.Generic.HashSet`1[[System.Boolean,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Byte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Byte[],System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Char,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.DateTime,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Decimal,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Double,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Guid,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Int16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Int32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Int64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.UInt16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.UInt32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.UInt64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.SByte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.Single,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.HashSet`1[[System.String,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Boolean,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Byte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Byte[],System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Char,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.DateTime,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Decimal,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Double,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Guid,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Int16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Int32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Int64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.UInt16,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.UInt32,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.UInt64,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.SByte,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.Single,System.Runtime]]" Dynamic="Required All" />
      <Type Name="System.Collections.Generic.List`1[[System.String,System.Runtime]]" Dynamic="Required All" />
	</Assembly>
	<Assembly Name="System.Runtime">
	  <Type Name="System.Nullable`1[System.Byte]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.SByte]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.UInt16]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Int32]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.UInt32]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Int16]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Int64]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.UInt64]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Single]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Double]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Decimal]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Char]" Dynamic="Required All"/>'
	  <Type Name="System.Nullable`1[System.DateTime]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Guid]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Enum]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.Boolean]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.IO.MemoryStream]" Dynamic="Required All"/>
	  <Type Name="System.Nullable`1[System.String]" Dynamic="Required All"/>
	</Assembly>
  </Application>
</Directives>

@pjmvp
Copy link

pjmvp commented Jan 27, 2024

Any updates on this one? Or maybe with regard to .NET 8?

@h4570
Copy link

h4570 commented Feb 24, 2024

In my scenario (dotnet8, managed runtime) I've used @branog68 modifications, but had to exclude these three lines due to compilation errors:

<Type Name="System.Nullable`1[System.Enum]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.IO.MemoryStream]" Dynamic="Required All"/>
<Type Name="System.Nullable`1[System.String]" Dynamic="Required All"/> 

After this fix, DynamoDbContext started working.

@Beau-Gosse-dev
Copy link

Although it doesn't fix the issue, this PR marked these as not compatible and has some explanation of what it would take to fix.

Also, Microsoft recommends moving away from rd.xml files as they are no longer support and instead using the trimming options here: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options?pivots=dotnet-8-0

@beeradmoore
Copy link

Hey @Beau-Gosse-dev , I may be missing it but can you point to where Microsoft are moving away from rd.xml? I can only see that it is not listed on that site at all, not that they recommend not using it.

@Beau-Gosse-dev
Copy link

@beeradmoore There are other reference from Michal, but here is one: dotnet/runtime#72989 (comment)

@beeradmoore
Copy link

Perfect. Thanks @Beau-Gosse-dev

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. dynamodb p2 This is a standard priority issue queued
Projects
None yet
Development

No branches or pull requests