Search code examples
c#android.netxamarin.formssqlite-net

SQLite-NET-PCL Stuck on CreateTableAsync<Type>()


I'm working on a Xamarin mobile app using .NET Framework, and SQLite.NET-PCL. My XAML Form uses MVVM architecture, on my main form I am attempting to display all movies in a ListView, first I made sure the view itself works correctly, and now I am attempting to connect it to my DB service. When the DB service initializes and attempts to create any table using CreateTableAsyc() the program gets stuck in that function even after appearing to succeed.

Initially, I thought it was due to using created classes as properties, which SQLite does not support, so I corrected that. Next, I checked if it was due to not using Hardware Acceleration so I enabled it. I then attempted to debug using ContinueWith() and printed the result to the debug console, it says 'Migrated' but never exits the function. Xamarin XAML Hot Reload then times out. What could be causing this?

Example of one of the Types:

using SQLite;

public class Movie
{
    public Movie() {}

    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime Length { get; set; }
}

DBService

using Xamarin.Essentials;

public static class DBService
{
    private static SQLiteAsyncConnection dbConn;

    public static async Task Init()
    {
        if (dbConn != null)
        {
            return;
        }

        string databasePath = Path.Combine(FileSystem.AppDataDirectory, "Movies.db");
        dbConn = new SQLiteAsyncConnection(databasePath, false);

        await dbConn.CreateTableAsync<Movie>().ContinueWith(results =>
        {
            Debug.WriteLine(results.Result.ToString()); // This prints migrated
        });
        Debug.WriteLine("After table created"); // This never prints
    }

    public static async Task<IEnumerable<Movie>> GetMovies()
    {
        await Init();
        return await dbConn.Table<Movie>().ToListAsync();
    }
}

MovieViewModel (View Model for main view)

public class MovieViewModel
{
    public List<Movie> Movies { get; set; }
    
    public MovieViewModel ()
    {
        Movies = (List<Movie>)DBService.GetMovies().Result;
    }
}

Solution

  • Wrap awaitable in something that runs after constructor returns:

    MainThread.BeginInvoke(async () => Movies = (await DBService.GetMovies()).ToList() );
    

    Optional:
    GetMovies could return a List, to match Movies declaration. Then would not need .ToList(), or (List<Movie>) cast:

    public static async Task<List<Movie>> GetMovies() { ... }
    
    MainThread.BeginInvoke(async () => Movies = await DBService.GetMovies() );