I have a SQLite database file called cbt.db
in the Data
folder in my .NET MAUI project. I'm trying to retrieve a collection of questions and answers from this database, but whenever I run the app I get a
System.NullReferenceException
on the line
var questions = await _dBService.GetQuestionsAsync();
in the QuizViewModel
.
QuizViewModel.cs
:
namespace CBT.ViewModels;
public partial class QuizViewModel : ObservableObject
{
private IDBService _dBService;
private int _currentQuestionIndex = 1;
public ObservableCollection<Question> Questions { get; } = new();
[ObservableProperty]
public Question? currentQuestion;
public QuizViewModel(IDBService dBService)
{
_dBService = dBService;
}
[RelayCommand]
public static async Task<QuizViewModel> CreateAsync(IDBService dBService)
{
var viewModel = new QuizViewModel(dBService);
await viewModel.GetQuestionsAsync();
viewModel.CurrentQuestion = viewModel.Questions[viewModel._currentQuestionIndex];
return viewModel;
}
[RelayCommand]
async Task GetQuestionsAsync()
{
try
{
var questions = await _dBService.GetQuestionsAsync();
if (questions.Count != 0)
{
Questions.Clear();
}
foreach(var question in questions)
{
Questions.Add(question);
}
}
catch(Exception ex)
{
Debug.Print($"Unable to get questions: {ex.Message}");
}
}
// Other methods go here...
}
DBService.cs
:
namespace CBT.Services;
class DBService : IDBService
{
private readonly SQLiteAsyncConnection _database;
public static string DBPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "cbt.db");
// The constructor initializes the database connection and creates the table if not exists
public DBService()
{
LoadDB();
_database = new SQLiteAsyncConnection(DBPath, Constants.Flags);
_database.CreateTableAsync<Question>();
}
public void LoadDB()
{
if (!File.Exists(DBPath))
{
var assembly = IntrospectionExtensions.GetTypeInfo(typeof(App)).Assembly;
using Stream? stream = assembly.GetManifestResourceStream("CBT.Data.cbt.db");
using MemoryStream? memoryStream = new();
stream.CopyTo(memoryStream);
File.WriteAllBytes(DBPath, memoryStream.ToArray());
Debug.WriteLine($"{stream}");
}
}
public async Task<List<Question>> GetQuestionsAsync()
{
return await _database.Table<Question>().ToListAsync();
}
// Other CRUD operations go here
}
MauiProgram.cs
:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.Services.AddSingleton<IDBService, DBService>();
builder.Services.AddSingleton<INavigationService, NavigationService>();
builder.Services.AddTransient<QuizViewModel>();
builder.Services.AddTransient<QuizPage>();
return builder.Build();
}
}
I finally figured out a solution. I was calling the DBService
before its initialization, so I initialized the QuizViewModel
in the QuizPage
and passed it the DBService
. Here is what it looked like.
QuizPage.xaml.cs
public partial class QuizPage : ContentPage
{
private QuizViewModel _viewModel;
public QuizPage(QuizViewModel quizViewModel)
{
InitializeComponent();
InitializeViewModel();
}
private async void InitializeViewModel()
{
// Create an instance of IDBService
var dbService = new DBService();
// Create an instance of QuizViewModel using CreateAsync method
_viewModel = await QuizViewModel.CreateAsync(dbService);
BindingContext = _viewModel;
}
}