I am having a strange NullReferenceException error when using dependency injection in my .NET MAUI 7 app. The API call to my API/token login URL works perfectly when I instantiate a new HttpClient for the request, but when using a service class, singletonService referencing that class in my builder and a view model to get passed the parameterless constructor requirement in maui, I am getting this error. I have tried other approaches too, such as a service locator approach instead of the view-model approach but it makes no difference. I'm starting to wonder if there is an issue with the .NET MAUI DI system.
Here is all my code for this issue:
MauiProgram.cs
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.Services.AddSingleton<IHttpService, HttpService>();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
HttpService.cs:
public interface IHttpService
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
// Add other HTTP methods as needed
}
public class HttpService : IHttpService
{
private readonly HttpClient _httpClient;
public HttpService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
return await _httpClient.SendAsync(request);
}
}
LoginPageViewModel.cs
public class LoginPageViewModel : BaseViewModel
{
private readonly IHttpService _httpService;
public LoginPageViewModel(IHttpService httpService)
{
_httpService = httpService;
}
// You can add more properties and methods specific to the view model
public IHttpService HttpService => _httpService;
}
LoginPage.xaml.cs (This is the page I recieve the error inside)
public partial class LoginPage : ContentPage
{
private string uText, pText;
public LoginPage()
{
InitializeComponent();
var httpService = DependencyService.Get<IHttpService>();
var viewModel = new LoginPageViewModel(httpService);
BindingContext = viewModel;
}
void OnEntryUsernameTextChanged(object sender, TextChangedEventArgs e)
{
uText = userNameEntry.Text;
}
void OnEntryUsernameTextCompleted(object sender, EventArgs e)
{
uText = ((Entry)sender).Text;
}
void OnEntryPasswordTextChanged(object sender, TextChangedEventArgs e)
{
pText = PasswordEntry.Text;
}
void OnEntryPasswordTextCompleted(object sender, EventArgs e)
{
pText = ((Entry)sender).Text;
}
protected override bool OnBackButtonPressed()
{
Application.Current.Quit();
return true;
}
private async void OnLoginClicked(object sender, EventArgs e)
{
var viewModel = (LoginPageViewModel)BindingContext;
string loginUsername = uText.Trim();
string loginPassword = pText.Trim();
//Make API call to login to the server
//This will be a call to recieve a token which is then attached to the rest
//of the API calls, as well as to check whether the user exists in the
//database and has entered correct login details
//HttpClient client = new HttpClient();
var request = new HttpRequestMessage(
HttpMethod.Get, "https://localhost:44386/token");
var collection = new List<KeyValuePair<string, string>>();
collection.Add(new("grant_type", "password"));
collection.Add(new("username", loginUsername));
collection.Add(new("password", loginPassword));
var content = new FormUrlEncodedContent(collection);
request.Content = content;
var response =
---> await viewModel.HttpService.SendAsync(request); <--- Null reference ex
await Task.Delay(3000);
if (response.IsSuccessStatusCode)
{
var token = await response.Content.ReadFromJsonAsync<Token>();
await SecureStorage.Default.SetAsync(
"accessTokenKey", token.access_token);
await Shell.Current.GoToAsync("///home");
}
else
{
await DisplayAlert("Error", response.StatusCode.ToString(), "Retry");
}
}
private async void OnRegisterPageClicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("///register");
}
}
I have tried many different approaches, even consulted Chat GPT to try get some advice but even Chat GPT is stumped with this one. I have done dependency injection on MVC apps and have never had issues like this.