Dependency Injection in .NET MAUI MVVM
Tip: Dependency Injection (DI) is a fundamental design pattern that promotes loosely coupled and maintainable code. It's crucial for building robust applications with .NET MAUI, especially within the MVVM architecture.
What is Dependency Injection?
Dependency Injection is a design pattern where a class receives its dependencies from an external source rather than creating them itself. Instead of a class instantiating its own dependencies, they are "injected" into the class, typically through its constructor, a property, or a method. This makes classes more modular, testable, and easier to manage.
Why Use DI in .NET MAUI?
- Testability: Makes it easy to substitute mock dependencies for unit testing.
- Maintainability: Reduces the coupling between classes, making code easier to modify and update.
- Reusability: Components become more independent and can be reused across different parts of the application.
- Organization: Promotes a cleaner, more structured codebase.
Integrating DI with .NET MAUI
.NET MAUI has built-in support for a lightweight dependency injection container. You configure and register your services in your application's startup code (e.g., MauiProgram.cs
).
Configuring the DI Container
The entry point for your .NET MAUI application is typically the MauiProgram.cs
file. Here, you can configure the services that will be available for injection.
MauiProgram.cs Example
using Microsoft.Extensions.DependencyInjection;
using YourAppName.Services;
using YourAppName.ViewModels;
using YourAppName.Views;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Register services
builder.Services.AddSingleton();
builder.Services.AddTransient();
// Register ViewModels
builder.Services.AddTransient();
builder.Services.AddTransient();
// Register Pages if needed (often done implicitly through navigation)
// builder.Services.AddTransient();
// builder.Services.AddTransient();
return builder.Build();
}
}
Registering Services
You can register services with different lifetimes:
AddSingleton<TService, TImplementation>()
: A single instance of the service is created and shared throughout the application's lifetime.AddTransient<TService, TImplementation>()
: A new instance of the service is created every time it's requested.AddScoped<TService, TImplementation>()
: (Less common in MAUI client-side apps, more for web) An instance is created per client operation or scope.
Injecting Dependencies into ViewModels
Once services are registered, you can inject them into your ViewModels via the constructor.
SampleViewModel.cs Example
using YourAppName.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class HomePageViewModel : ObservableObject
{
private readonly IMessageService _messageService;
private readonly ISampleService _sampleService;
[ObservableProperty]
private string _message;
public HomePageViewModel(IMessageService messageService, ISampleService sampleService)
{
_messageService = messageService;
_sampleService = sampleService;
LoadMessageCommand.Execute(null); // Load initial message
}
[RelayCommand]
private async Task LoadMessage()
{
Message = await _messageService.GetWelcomeMessageAsync();
// You can also use _sampleService here for other operations
var data = _sampleService.GetData();
System.Diagnostics.Debug.WriteLine($"Sample service data: {data}");
}
}
Resolving Dependencies for Pages/Views
When using MVVM, your pages typically instantiate ViewModels. The .NET MAUI DI container can resolve these ViewModels, and because the ViewModels depend on services, those services will also be resolved and injected automatically.
HomePage.xaml.cs Example
using YourAppName.ViewModels;
public partial class HomePage : ContentPage
{
// The HomePageViewModel is resolved and injected by the DI container
public HomePage(HomePageViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}
Important: When you use AddTransient
for a ViewModel, a new instance is created each time the page is navigated to. For singletons or objects that should persist across navigation, use AddSingleton
or consider a different approach for managing ViewModel instances.
Advanced DI Concepts
- Named Services: For registering multiple implementations of the same interface.
- Service Provider: You can access the service provider to manually resolve services if needed, though constructor injection is preferred.
- Advanced Lifetimes: Understanding how different lifetimes affect object creation and sharing.
Conclusion
Leveraging Dependency Injection in your .NET MAUI MVVM applications is a powerful way to write clean, testable, and maintainable code. By configuring the DI container in MauiProgram.cs
and injecting services into your ViewModels and Pages, you can build more robust and scalable cross-platform applications.