.Net Maui Understanding Dependency Injection In Maui Complete Guide
Understanding the Core Concepts of .NET MAUI Understanding Dependency Injection in MAUI
What is Dependency Injection?
Dependency Injection is a design pattern where an object or method receives other objects or methods (its dependencies) as parameters rather than creating them itself. In .NET MAUI, the built-in DI container (Microsoft.Extensions.DependencyInjection
) is used to manage these dependencies.
Key Benefits of Dependency Injection
- Decoupling: Objects are decoupled from their dependencies, allowing changes to be made independently.
- Testability: Easier to write unit tests since dependencies can be easily mocked.
- Maintainability: Simplifies maintaining and updating application architecture by making dependencies explicit.
- Flexibility: Facilitates the substitution of dependencies with different implementations at runtime.
Setting Up Dependency Injection in .NET MAUI
At the core of DI setup in MAUI is the MauiAppBuilder
. Here’s how you can set up DI:
- Create Services Collection: Use
MauiAppBuilder
to add services to the DI container. - Add Transient, Singleton or Scoped Services: Define how long the instance of each service will live.
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// Register Services
builder.Services.AddSingleton<IMyService, MyService>();
builder.Services.AddTransient<IMyTransientService, MyTransientService>();
return builder.Build();
}
Service Lifetimes in .NET MAUI
Understanding service lifetimes is critical for efficiently managing resources and ensuring that instances are created and disposed correctly:
Singleton: There is only one instance throughout the application lifecycle. Use this for services that do not hold any state.
- Example: Logger services.
builder.Services.AddSingleton<IMyService, MyService>();
Scoped: A new instance is created for every scope (typically per request in web apps, but can also be per user, session, etc., depending on your app). Within a view model, scoped services are usually per-view-model.
- Example: User-specific settings or contexts.
builder.Services.AddScoped<IMyScopedService, MyScopedService>();
Transient: A new instance is created every time the dependency is injected. This is useful for non-shared resources.
- Example: Commands or operations that do not need to persist state.
builder.Services.AddTransient<IMyTransientService, MyTransientService>();
Resolving Dependencies
Dependencies are resolved using constructors or property injection, though constructor injection is preferred in .NET MAUI due to its simplicity and clarity.
Constructor Injection
Pass dependencies as constructor parameters:
public class MainPageViewModel
{
private readonly IMyService _myService;
public MainPageViewModel(IMyService myService)
{
_myService = myService;
}
public void DoSomething()
{
_myService.SomeMethod();
}
}
This approach keeps the view model's dependencies explicit and easier to manage.
Property Injection
Less common in .NET MAUI but can be useful when dealing with optional dependencies:
public class MainPageViewModel
{
[ActivatorUtilitiesConstructor]
public MainPageViewModel() {}
public IMyService MyService { get; set; }
public void DoSomething()
{
MyService?.SomeMethod();
}
}
However, be wary that property injection can lead to less obvious and tighter coupling if not managed properly.
DI with MVVM Pattern
.NET MAUI often uses the Model-View-ViewModel (MVVM) pattern, where resolving dependencies plays a significant role. The .NET MAUI framework provides built-in support for DI with MVVM.
ViewModel Registration: ViewModels can be registered like any other service:
builder.Services.AddTransient<MainPageViewModel>();
View Binding: Views can bind to ViewModels through
BindingContext
, which can be automatically set via the DI container:public MainPage() { InitializeComponent(); BindingContext = MauiProgram.Current.Services.GetService<MainPageViewModel>(); }
Alternatively, consider using
Maui.Controls.Extensions.Hosting
for automatic binding, which reduces boilerplate:builder.Services.AddTransient<MainPage>(); builder.Services.AddTransient<MainPageViewModel>(); builder.UseMauiHosting() .ConfigureServices(services => { services.AddTransient<IViewFor<MainPageViewModel>, MainPage>(); }) .ConfigureMauiHandlers(handlers => { handlers.AddHandler(typeof(IViewFor<>), typeof(ViewHandler)); });
Using the IServiceProvider
The .NET MAUI DI container implements IServiceProvider
, allowing you to access services anytime within the application:
var myService = MauiProgram.Current.Services.GetService<IMyService>();
However, be cautious about using the service provider directly in your application logic. It makes code harder to test and understand.
Example
Here's a simple example to illustrate DI in a .NET MAUI MVVM application:
Interfaces and Services
public interface IMyService
{
string GetData();
}
public class MyService : IMyService
{
public string GetData() => "Hello from MyService!";
}
ViewModel
public class MainPageViewModel
{
private readonly IMyService _myService;
public string DisplayData { get; set; }
public MainPageViewModel(IMyService myService)
{
_myService = myService;
DisplayData = _myService.GetData();
}
}
AppBuilder Setup
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// Register Services
builder.Services.AddSingleton<IMyService, MyService>();
// Register ViewModel
builder.Services.AddTransient<MainPageViewModel>();
// Register View and Bind ViewModel
builder.Services.AddTransient<IViewFor<MainPageViewModel>, MainPage>();
return builder.Build();
}
Main Page XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyMauiApp.MainPage"
xmlns:viewmodel="clr-namespace:MyMauiApp.ViewModel">
<ContentPage.BindingContext>
<viewmodel:MainPageViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Label Text="{Binding DisplayData}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"/>
</StackLayout>
</ContentPage>
Conclusion
Dependency Injection in .NET MAUI provides a robust framework for building modular, maintainable, and testable applications. By leveraging the MauiAppBuilder
to register services with appropriate lifetimes and using constructor injection for view models, developers can create applications that are easier to understand, scale, and modify.
Online Code run
Step-by-Step Guide: How to Implement .NET MAUI Understanding Dependency Injection in MAUI
Step 1: Create a New .NET MAUI Application
First, you need to create a new .NET MAUI project. You can do this using Visual Studio:
- Open Visual Studio.
- Click on
Create a new project
. - Select
MAUI App
and clickNext
. - Fill in the project details (name, location, solution name) and click
Create
. - Wait for Visual Studio to create the new project.
Step 2: Define an Interface and a Concrete Implementation
You’ll need an interface and a class that implements the interface. This is where the magic of DI comes in.
Create an Interface
In your project, create a new folder called Services
and add a new interface inside it.
ISampleService.cs
namespace YourAppNamespace.Services
{
public interface ISampleService
{
string GetMessage();
}
}
Create a Concrete Implementation
Now, create a class that implements the ISampleService
interface.
SampleService.cs
namespace YourAppNamespace.Services
{
public class SampleService : ISampleService
{
public string GetMessage()
{
return "Hello from DI!";
}
}
}
Step 3: Register the Service in the Dependency Injection Container
In .NET MAUI, the MauiProgram.cs
file is where you can configure the dependency injection container.
Open MauiProgram.cs
and locate the Build
method. Register the ISampleService
with its implementation SampleService
.
MauiProgram.cs
using Microsoft.Extensions.DependencyInjection;
using YourAppNamespace;
using YourAppNamespace.Services;
namespace YourAppNamespace
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// Register the service with DI container
builder.Services.AddSingleton<ISampleService, SampleService>();
return builder.Build();
}
}
}
Step 4: Inject the Service into a Page
Now, you can inject the ISampleService
into your MAUI page to utilize it.
Modify MainPage.xaml.cs
Open MainPage.xaml.cs
and modify it to use the ISampleService
.
MainPage.xaml.cs
using System.ComponentModel;
using Microsoft.Maui.Controls;
namespace YourAppNamespace
{
public partial class MainPage : ContentPage
{
private readonly ISampleService _sampleService;
public MainPage(ISampleService sampleService)
{
InitializeComponent();
_sampleService = sampleService;
BindingContext = new MainViewModel(_sampleService);
}
protected override void OnAppearing()
{
base.OnAppearing();
// You can call the service here if needed, or let the ViewModel handle it
}
}
public class MainViewModel : INotifyPropertyChanged
{
private readonly ISampleService _sampleService;
private string _message;
public string Message
{
get => _message;
set
{
_message = value;
OnPropertyChanged(nameof(Message));
}
}
public MainViewModel(ISampleService sampleService)
{
_sampleService = sampleService;
LoadMessage();
}
private void LoadMessage()
{
Message = _sampleService.GetMessage();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Update MainPage.xaml
Modify MainPage.xaml
to display the message.
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:YourAppNamespace"
x:Class="YourAppNamespace.MainPage">
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="{Binding Message}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
FontSize="Medium"
Margin="10"/>
</StackLayout>
</ContentPage>
Step 5: Run the Application
Now, you can run your application to see the magic in action.
- Press
F5
or click theRun
button in Visual Studio. - The application should launch and display the message "Hello from DI!" on the main screen.
Conclusion
In this example, you learned how to:
- Define an interface and a concrete implementation.
- Register the service in the dependency injection container.
- Inject the service into a page and use it to display data.
Login to post a comment.