0

Trying to create a MAUI App using SQLLite database service, and for the Database service to be used by both UI pages and WorkManager background workers. I have registered the Database service as a Singleton DI Service but when the WorkManager Workers and / or UI pages ( main thread ) construct themselves when needing the Database Service , the DI Context seems to be different and it creates 2 Database services. I was hoping it could be shared meaning one Singleton Database service for the whole app as to control better the single SQLLite writer limitation.

Not sure how else to let UI and background workers use the database service without running into concurrency or database corruption issues due to both trying to write data at the same time.

I tried various MauiProgram.cs DI Service registration variations and change of timings regarding using a CustomWorkManager and either initializing it in the MainApplication.cs or outside the Mauiprogram.cs as part of this code .


    public static MauiApp CreateMauiApp()
      {
          var builder = MauiApp.CreateBuilder();
          builder.Services.AddSingleton<Services.IDatabaseService,Services.DatabaseService>();
    
    #if ANDROID
          builder.Services.AddSingleton<CustomWorkManagerFactory>();
          builder.Services.AddSingleton<Platforms.Android.Sync.SyncWorker>();
    #endif
          var app = builder.Build();
          Helpers.ServiceLocator.ServiceProvider = builder.Services.BuildServiceProvider();
    #if ANDROID
          var context = Android.App.Application.Context;
          var customFactory =    Helpers.ServiceLocator.ServiceProvider.GetRequiredService<Platforms.Android.Helpers.CustomWorkManagerFactory>();
          var configuration = new AndroidX.Work.Configuration.Builder()
             .SetWorkerFactory(customFactory)
             .Build();
          AndroidX.Work.WorkManager.Initialize(context, configuration);
    #endif
       return app;

Also inside the Database Service the write logic is protected by Semaphoreslims eg 

    public async Task<int> InsertDB(object item) { 
await _writeSemaphore.WaitAsync(); try { int result = await Database().InsertAsync(item); return result; } catch (Exception ex) { throw; } finally { _writeSemaphore.Release(); } } 

But whatever I try, it seems to construct a new DatabaseService when a worker is created or when UI kicks off the show pages. It never finds / re uses the Singleton Database Service created by the 1st process that instantiated it. Perhaps its impossible as they run in different memory spaces etc.

Can it be done? if so looking forward receiving suggestions.

Ultimately its about keeping the single SQLLite writer in check across the whole app but like I said I thought having a DI Singleton Service together with the SemaphoreSlim would do the trick. Accurate / independent database activity across all parts of the app is the main requirement to get right.

Thank you for any suggestions.

4
  • Not sure how else to let UI and background workers use the database service without running into concurrency or database corruption issues due to both trying to write data at the same time. How does having a singleton stop this? Am I missing something? You need to use a lock to avoid this as far as I know and if you are using SqliteAsync then you need to use SemaphoreSlim Commented Dec 17, 2024 at 9:57
  • Within the Database service its using SemaphoreSlim eg public async Task<int> InsertDB(object item) { await _writeSemaphore.WaitAsync(); try { int result = await Database().InsertAsync(item); return result; } catch (Exception ex) { throw; } finally { _writeSemaphore.Release(); } } but if it creates 2 database services then there will be 2 locks still and so 2 SQLLite writers can still occur. My understanding of managing 1 SQLLite writer across threads is limited. Commented Dec 17, 2024 at 10:03
  • When you register the Database service as a Singleton, it should ideally create only one instance of the service. However, if different DI contexts are being used, it might lead to multiple instances. Ensure that the DI container is correctly configured, and that the same container is used across the entire application, including both the UI and background workers. Commented Dec 23, 2024 at 1:42
  • Besides, SQLite has a limitation of a single writer at a time, which can lead to concurrency issues. To mitigate this, you can use a combination of locking mechanisms and careful management of database connections. For instance, you can use a SemaphoreSlim to control access to the database, ensuring that only one thread can write to the database at a time. Commented Dec 23, 2024 at 1:43

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.