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

Synchronization Not Working in Release Mode with Dotmim.Sync on MAUI App (AOT Compilation Error) #1268

Open
JustAnotherLars opened this issue Oct 30, 2024 · 10 comments

Comments

@JustAnotherLars
Copy link

JustAnotherLars commented Oct 30, 2024

Description:

I'm using Dotmim.Sync as a singleton service in a .NET MAUI app to handle data synchronization. The issue I’m experiencing is that synchronization works perfectly in Debug mode but fails in Release mode.
In Release mode, no synchronized objects are visible in the app, though the app itself doesn't crash. The following error message appears in the logs during synchronization attempts:

The error seems related to JIT compilation, which is restricted in AOT-only environments. I've tried enabling the Mono Interpreter for Release builds as a workaround:

<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'"> <UseInterpreter>true</UseInterpreter> </PropertyGroup>
However, with this setting, the app fails to start entirely in Release mode.

Description:

I'm using Dotmim.Sync as a singleton service in a .NET MAUI app to handle data synchronization. The issue I’m experiencing is that synchronization works perfectly in Debug mode but fails in Release mode.

In Release mode, no synchronized objects are visible in the app, though the app itself doesn't crash. The following error message appears in the logs during synchronization attempts:

Dotmim.Sync.SyncException: [InternalEnsureScopeInfoAsync]..[InternalExistsScopeInfoAsync]..Attempting to JIT compile method '(wrapper dynamic-method) object object:Thunk1ret_Object_CallSite_Object_Object (System.Func`2<object[], object>,System.Runtime.CompilerServices.CallSite,object,object)' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.

 ---> System.ExecutionEngineException: 

   at System.Delegate.CreateDelegate(Type , Object , MethodInfo , Boolean , Boolean )
   at System.Reflection.Emit.DynamicMethod.CreateDelegate(Type , Object )
   at System.Dynamic.Utils.DelegateHelpers.CreateObjectArrayDelegateRefEmit(Type , Func`2 )
   at System.Dynamic.Utils.DelegateHelpers.CreateObjectArrayDelegate(Type , Func`2 )
   at System.Linq.Expressions.Interpreter.LightLambda.MakeDelegate(Type )
   at System.Linq.Expressions.Interpreter.LightDelegateCreator.CreateDelegate(IStrongBox[] )
   at System.Linq.Expressions.Expression`1[[System.Func`4[[System.Runtime.CompilerServices.CallSite, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Compile()
   at System.Runtime.CompilerServices.CallSite`1[[System.Func`4[[System.Runtime.CompilerServices.CallSite, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].CreateCustomNoMatchDelegate(MethodInfo )
   at System.Runtime.CompilerServices.CallSite`1[[System.Func`4[[System.Runtime.CompilerServices.CallSite, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MakeUpdateDelegate()
   at System.Runtime.CompilerServices.CallSite`1[[System.Func`4[[System.Runtime.CompilerServices.CallSite, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].GetUpdateDelegate(Func`4& )
   at System.Runtime.CompilerServices.CallSite`1[[System.Func`4[[System.Runtime.CompilerServices.CallSite, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].GetUpdateDelegate()
   at System.Runtime.CompilerServices.CallSite`1[[System.Func`4[[System.Runtime.CompilerServices.CallSite, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]..ctor(CallSiteBinder )
   at System.Runtime.CompilerServices.CallSite`1[[System.Func`4[[System.Runtime.CompilerServices.CallSite, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Create(CallSiteBinder binder)
   at Dotmim.Sync.SyncTypeConverter.TryConvertTo[String](Object value, CultureInfo provider)
   at Dotmim.Sync.SyncTypeConverter.TryConvertFromDbType(Object value, DbType typeOfT, CultureInfo provider)
   at Dotmim.Sync.BaseOrchestrator.InternalSetParameterValue(DbCommand command, String parameterName, Object value)
   at Dotmim.Sync.BaseOrchestrator.InternalExistsScopeInfoAsync(String scopeName, SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress`1 progress)
   at Dotmim.Sync.BaseOrchestrator.InternalExistsScopeInfoAsync(String scopeName, SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress`1 progress)
   Exception_EndOfInnerExceptionStack
   at Dotmim.Sync.LocalOrchestrator.InternalEnsureScopeInfoAsync(SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress`1 progress)
   at Dotmim.Sync.SyncAgent.SynchronizeAsync(String scopeName, SyncSetup setup, SyncType syncType, SyncParameters parameters, CancellationToken cancellationToken, IProgress`1 progress)
   at DigitalMeasurementProtocol.MAUI.Services.SyncService.Sync()

The error seems related to JIT compilation, which is restricted in AOT-only environments. I've tried enabling the Mono Interpreter for Release builds as a workaround:

<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
    <UseInterpreter>true</UseInterpreter>
</PropertyGroup>

However, with this setting, the app fails to start entirely in Release mode.

Steps to Reproduce:
Configure Dotmim.Sync as a singleton in a .NET MAUI app.
Run the synchronization in Debug mode - it works as expected.
Deploy the app in Release mode on an iOS device.
Observe the error in the log file.

Expected Behavior:
Synchronization should work in both Debug and Release modes without needing JIT compilation.

Actual Behavior:
In Debug mode, synchronization works as expected.
In Release mode, the synchronization fails due to the AOT restriction, and enabling the Mono Interpreter prevents the app from starting.

Version Information:
Dotmim.Sync.Core: 1.2.0
Dotmim.Sync.Sqlite: 1.2.0
Dotmim.Sync.Web.Client: 1.2.0
.NET MAUI: net8.0
Platform: iOS (Release mode)

Workarounds Attempted:
Enabled Mono Interpreter for Release builds (true), but the app fails to start.

Additional Context: Any advice on using Dotmim.Sync in AOT environments like iOS in Release mode, or if alternative configuration settings could resolve the AOT compilation issues, would be appreciated.

@JustAnotherLars
Copy link
Author

By the way, this function causes the error.

        public async Task Sync()
        {
            if(Connectivity.NetworkAccess != NetworkAccess.Internet)
            {
                _logger.LogInformation("No Internetconnection available. Sync aborted.");
                return;
            }
            try
            {
                var token = await _authorizationService.GetAuthenticationTokenAsync();
                var httpHandler = new HttpClientHandler
                {
                    AutomaticDecompression = System.Net.DecompressionMethods.GZip
                };
                HttpClient httpClient = new HttpClient(httpHandler);
                httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.Token);
                httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
                var dbFile = Helpers.Constants.DbPath;
                if(dbFile is null)
                {
                    _logger.LogInformation("No database file found.");
                    return; 
                }
                _remoteOrchestrator = new WebRemoteOrchestrator(Constants.ServerAdr, client: httpClient);
                _syncProvider = new SqliteSyncProvider(dbFile);
                _syncAgent = new SyncAgent(_syncProvider, _remoteOrchestrator);
                var setup = new SyncSetup(new string[] { "User", "Project", "Measurement", "AbstractBaseEquipement", "Media", "AbstractBaseShape", "MeasurementLocation", "ProjectUser", "Line" });
                var result = await _syncAgent.SynchronizeAsync(setup);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "An error occured during synchronization.");
                throw;
            }
        }

@generic823
Copy link

@Mimetis I wanted to ask if the Dotmim framework supports MAUI on iOS. I have been using this framework for the past six months to build my application, and I am close to completion. However, I am encountering the same error that JustAnotherLars mentioned, and I haven't seen a response addressing this issue yet. I thought it would be best to reach out and inquire whether this issue has been resolved or if there are plans to resolve it before making any further plans for my app. Thank you!

@Mimetis
Copy link
Owner

Mimetis commented Jan 1, 2025

I have no idea, I never tried or used AOT compilation

@mpnow
Copy link

mpnow commented Jan 1, 2025

@JustAnotherLars @generic823 I asked Claude about the above error and it returned the following info - is this helpful?


This error occurs because Dotmim.Sync is using dynamic code generation/reflection (specifically trying to JIT compile a dynamic method) which isn't supported in iOS AOT mode.

To fix:

  1. Add relevant types to linker configuration:
<linker>
  <assembly fullname="Dotmim.Sync">
    <type fullname="Dotmim.Sync.SyncTypeConverter" preserve="all"/>
  </assembly>
</linker>
  1. Or disable AOT for problematic assemblies in iOS project settings:
<MtouchExtraArgs>--linkskip=Dotmim.Sync</MtouchExtraArgs>

Choose option 1 for better performance, option 2 for simpler implementation.

@mpnow
Copy link

mpnow commented Jan 7, 2025

I tried both of Claude's suggestions and unfortunately neither of them fixed the error. @Mimetis do you have any more thoughts/suggestions? Alas at this point it appears I'm going to have to go find another sync solution to use.

@Mimetis
Copy link
Owner

Mimetis commented Jan 7, 2025

Can you share a sample (a really simple sample that highlight the failure), preferably a GitHub repository, I can use to reproduce the error ?

@mpnow
Copy link

mpnow commented Jan 7, 2025

I might be able to. @JustAnotherLars are you able to do that?

@Mimetis just realize that you'll have to create an iOS Distribution certificate and Ad-hoc provisioning profile, and deploy it to an iOS device, in order to see the issue; are you in a position to be able to do that? I don't believe there's any way around that.

@Mimetis
Copy link
Owner

Mimetis commented Jan 7, 2025

No I don't have an iOS device, I'm running an Android phone, unfortunately.

Can we reproduce the error, using the release mode, and the android emulator ?

@mpnow
Copy link

mpnow commented Jan 7, 2025

No, it's specifically an iOS issue. So a small sample won't do you any good. Not to be a negative nellie but it seems unlikely you'll be able to repro the error without an iOS device to deploy to. @JustAnotherLars or @generic823, do you have any other ideas?

@mpnow
Copy link

mpnow commented Jan 8, 2025

Here is detailed info on how someone got that other sync toolkit to work; @JustAnotherLars or @Mimetis, would this info help to resolve the issue here as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants