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

Native Aot Support #135

Open
richard-einfinity opened this issue Oct 25, 2024 · 12 comments
Open

Native Aot Support #135

richard-einfinity opened this issue Oct 25, 2024 · 12 comments
Labels
Client Improvements or additions to the client code Feature Request New functionality is needed
Milestone

Comments

@richard-einfinity
Copy link
Contributor

Our Maui based solution is using Native Aot and reflection based serializarion is disabled for Aot projects.

I think this would mean exposing the serialization settings for the offline entities and / or providing a registration mechanism for providing JsonTypeInfo for the entities.

Happy to look into and propose possible solutions if it's not already on the radar.

@richard-einfinity richard-einfinity added Feature Request New functionality is needed Requires Triage This issue has not been checked by the project team. labels Oct 25, 2024
@richard-einfinity
Copy link
Contributor Author

richard-einfinity commented Oct 28, 2024

So I've done a bit of digging and Serialization options are all internal. I think the crux of this is we need a way of setting the TypeInfoResolver on the SerializerOptions. So that the resolver can be passed from the project specific JsonSerializerContext sub class.

The options specified for the JsonSourceGenerationOptions need to match the current options constructed in the DatasyncSerializer class.

A JsonSerializable attribute will need to be added for each offline Entity to the JsonSerializerContext sub class it will need to include the Page class as well.

[JsonSerializable(typeof(Page<OfflineEntity1>))]
[JsonSerializable(typeof(Page<OfflineEntity2>))]
[JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    UseStringEnumConverter = true, WriteIndented = false, GenerationMode = JsonSourceGenerationMode.Metadata, AllowTrailingCommas = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, Converters = [ 
        typeof(JsonStringEnumConverter), typeof(DateTimeConverter), typeof(DateTimeOffsetConverter), typeof(TimeOnlyConverter), typeof(MicrosoftSpatialGeoJsonConverter)])]
public partial class OfflineSerializerContext : JsonSerializerContext
{

}

I think my proposal would be to add a property to the OfflineOptions class for the serializer options and add a Method to the DatasyncOfflineOptionsBuilder for UseSerializerOptions. Then use the OfflineOptions property to set the JsonSerializerOptions on the base OfflineDbContext class.

Maybe default the settings to the currentsettings to avoid breaking changes.

@adrianhall
Copy link
Collaborator

Let me look at this more deeply on Friday (when I get a chance to work on this project). If it’s that simple, it should be easy to expose. However, what would be ideal is a project that “works when no Native AOT, but doesn’t work when Native AOT” is supplied as a cli able GitHub repository with instructions on how to adjust the project for Native AOT.

Basically, I’m not a MAUI expert, so I can do the minimum needed to get something working.

@adrianhall adrianhall added Client Improvements or additions to the client code and removed Requires Triage This issue has not been checked by the project team. labels Oct 31, 2024
@richard-einfinity
Copy link
Contributor Author

Hi @adrianhall unfortunately for Maui native Aot can't be disabled for iOS and catalyst https://learn.microsoft.com/en-us/dotnet/maui/macios/interpreter?view=net-maui-8.0

It's thrown up a few issues to say the least, even with efcore you have to use compiled models... which is a requirement for Aot. UseModel is a configuration option for the DbContextBuilder

The ef tools don't run against a Maui project so you need segregate projects to run the tools. Which you need to generate the compiled modules and migrations etc.

I'll document all the sticking points but I think at the moment it hasn't been an easy move from azure mobile apps. And this issue would block a release targeting iOS for anyone using Maui looking to migrate.

I'll update the sample project in my fork for reference

@richard-einfinity
Copy link
Contributor Author

Hi @adrianhall just looking at this and mstest now supports testing with native Aot https://devblogs.microsoft.com/dotnet/testing-your-native-aot-dotnet-apps/

Will this support what your looking for in terms of a cli able application or were you thinking more of a console app that would work not Aot and then fail with Aot?

Cheers

Rich

@adrianhall
Copy link
Collaborator

Not sure right now :-)

I am thinking of working on the MAUI Todo app and turning on Native AOT. Not sure if that will work for Android, but if it does, it should be enough to show off the problem and I can go from there.

@adrianhall
Copy link
Collaborator

FWIW, I believe that this is relatively easy.

  1. Set DatasyncSerializer to be public.
  2. Set the JsonSerializerOptions to be public read.

That;s pretty much it. Then, in your code, you would do something like:

DatasyncSerializer.JsonSerializerOptions.<whatever your changes are>

It may also be worth my while to specify this in the EF Core extensions I have. Right now, I copy the reference to the JsonSerializerOptions from the DatasyncSerializer to the DbContext when it starts up; thus setting the DatasyncSerializer is the right thing to do.

If you want to test it, it’s a 2 line change to the library. I can put it in a branch for you to test (probably tonight - assuming I can get my desktop back online tonight - it fried its brain this week and I had to replace the SSD)

@richard-einfinity
Copy link
Contributor Author

richard-einfinity commented Nov 2, 2024

Will the crashlytics logs help? Also you can set a project property to emulate the Aot behaviour for the serializer it won't affect the DbContext though.

https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation?pivots=dotnet-8-0#disable-reflection-defaults

<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>

@richard-einfinity
Copy link
Contributor Author

I feel your pain, spilt a can of Pepsi on my surface keyboard Monday evening... this week has been interesting.

@richard-einfinity
Copy link
Contributor Author

Hi @adrianhall

I've updated the samples in a branch in my repository Updated Samples

It seems there is Native AoT and Native AoT. The async extensions for EF Core use MakeGenericMethod under the hood so they fail on native AoT with the console app. So this method fails.

/// <summary>
/// An asynchronous process for getting the last sequence ID used by the operations queue.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe.</param>
/// <returns>The last sequence ID in use, <c>0L</c> by default.</returns>
internal Task<long> GetLastSequenceIdAsync(CancellationToken cancellationToken = default)
    => OperationsQueue.OrderByDescending(x => x.Sequence).Select(x => x.Sequence).FirstOrDefaultAsync(cancellationToken);

For Android there's a hard fail if reflection based serialization is enabled when syncing and if your not using a compiled model for the context. It doesn't seem to care about anything else.

For iOS it has the same hard fails as Android but complains about dynamic code generation in EFCore. Enabling the MTouchInterpreter seems to get over this.

The amendments in the samples and exposing the serializer options get passed the hard fails.

@adrianhall adrianhall added this to the 8.0.4 milestone Nov 13, 2024
adrianhall added a commit to adrianhall/CommunityToolkit-Datasync that referenced this issue Nov 13, 2024
@adrianhall
Copy link
Collaborator

I've just checked in a change that I think will fix this issue by allowing you to set the DatasyncSerializer.JsonSerializerOptions. Explicitly:

  • On Startup, the DatasyncSerializer.JsonSerializerOptions is set to the default settings.
  • You can override this by either setting DatasyncSerializer.JsonSerializerOptions.
  • You can get the default JsonSerializerOptions using DatasyncSerializer.GetJsonSerializerOptions().
  • If you set DatasyncSerializer.JsonSerializerOptions to null, the default set of options is used again.

So, my belief is that if you do something like this:

JsonSerializerOptions options = DatasyncSerializer.JsonSerializerOptions();
// TODO: Set the TypeInfo 
DatasyncSerializer.JsonSerializerOptions = options

Before you make any call into Datasync, it should do the right thing.

@richard-einfinity Can you validate that this fixes your problem?

I don't see what you did with the updated samples; perhaps doing a PR to update the samples will help us review them and see what is going on?

@richard-einfinity
Copy link
Contributor Author

@adrianhall Hi Adrian. I made a similar change in my fork and yes it worked for the serializer issue. There was a few more changes that had to be made for compiling in release mode.

EF has to use compiled models. Compiled Models

The tools won't work against the Maui project so they need to be put into a separate library project targeting net8. You need a dummy start up project then to reference the library project when running the tools.

Doesn't look like Native AoT support is coming to EF 8 there seem to be some enhancements in EF9 . As i mentioned in a previous comment it seems we have difference flavours of Native AoT on the different platforms. Android, Apple and Windows all seem to have different outcomes. The changes I have made gets everything running again for my project and in the samples, in particular on iOS where it was blocking.

I think the wider issue of AoT support will need to be parked probably until net / EF 10 in line with the support policy of the project. Hopefully there will be more support in the underlying frameworks then.

@adrianhall adrianhall modified the milestones: 8.0.4, 9.0.0 Nov 15, 2024
@adrianhall
Copy link
Collaborator

Shifting the rest of the work to 9.0.0 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Client Improvements or additions to the client code Feature Request New functionality is needed
Projects
None yet
Development

No branches or pull requests

2 participants