All skills
davidortinau avatar

/maui-dependency-injection

@542317e

Guidance for dependency injection in .NET MAUI apps — service registration, lifetime selection (Singleton/Transient/Scoped), constructor injection, automatic resolution via Shell navigation, explicit resolution patterns, platform-specific registrations, and testability best practices. USE FOR: "dependency injection", "DI registration", "AddSingleton", "AddTransient", "AddScoped", "service registration", "constructor injection", "IServiceProvider", "MauiProgram DI", "register services". DO NOT USE FOR: data binding (use maui-data-binding), Shell route setup (use maui-shell-navigation), or unit test mocking patterns (use maui-unit-testing).

Use this Skill: https://skilld.dev/gh/davidortinau/maui-skills/maui-dependency-injection

This session only. Nothing lands on disk.

referencesdependency-injection-api.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Dependency Injection API Reference

Service Registration in MauiProgram.cs

Register services on builder.Services inside CreateMauiApp():

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder.UseMauiApp<App>();

    // Services
    builder.Services.AddSingleton<IDataService, DataService>();
    builder.Services.AddTransient<IApiClient, ApiClient>();

    // ViewModels
    builder.Services.AddTransient<MainViewModel>();
    builder.Services.AddTransient<DetailViewModel>();

    // Pages
    builder.Services.AddTransient<MainPage>();
    builder.Services.AddTransient<DetailPage>();

    return builder.Build();
}

Lifetime Reference

Lifetime Use When Examples
AddSingleton<T> Shared state, expensive to create, or app-wide config Database connection, settings service, HttpClient factory
AddTransient<T> Stateless, lightweight, or per-request usage ViewModels, pages, API call wrappers
AddScoped<T> Per-scope lifetime (rarely used in MAUI — no built-in scope per page) Scoped unit-of-work in manually created scopes

Constructor Injection

Inject dependencies through the constructor. The DI container resolves them automatically when the type is itself resolved from the container:

public class MainViewModel
{
    private readonly IDataService _dataService;
    private readonly IApiClient _apiClient;

    public MainViewModel(IDataService dataService, IApiClient apiClient)
    {
        _dataService = dataService;
        _apiClient = apiClient;
    }
}

ViewModel → Page Pattern

Register both the ViewModel and the Page. Inject the ViewModel into the Page constructor and assign it as BindingContext:

public partial class MainPage : ContentPage
{
    public MainPage(MainViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}

Automatic Resolution via Shell Navigation

When Pages are registered in the DI container and registered as Shell routes, Shell resolves them (and their dependencies) automatically:

// In MauiProgram.cs
builder.Services.AddTransient<DetailPage>();
builder.Services.AddTransient<DetailViewModel>();

// Route registration (AppShell.xaml.cs or startup)
Routing.RegisterRoute(nameof(DetailPage), typeof(DetailPage));

// Navigation — DI resolves DetailPage and its DetailViewModel
await Shell.Current.GoToAsync(nameof(DetailPage));

Explicit Resolution

When constructor injection is not available, resolve services explicitly:

// From any Element with a Handler
var service = this.Handler.MauiContext.Services.GetService<IDataService>();

IServiceProvider Injection

Inject IServiceProvider when you need to resolve services dynamically:

public class MyService
{
    private readonly IServiceProvider _serviceProvider;

    public MyService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void DoWork()
    {
        var api = _serviceProvider.GetRequiredService<IApiClient>();
    }
}

Platform-Specific Service Registration

Use preprocessor directives to register platform-specific implementations:

// In MauiProgram.cs
#if ANDROID
builder.Services.AddSingleton<INotificationService, AndroidNotificationService>();
#elif IOS || MACCATALYST
builder.Services.AddSingleton<INotificationService, AppleNotificationService>();
#elif WINDOWS
builder.Services.AddSingleton<INotificationService, WindowsNotificationService>();
#endif

Interface-First Pattern for Testability

Define interfaces for services so implementations can be swapped in tests:

public interface IDataService
{
    Task<List<Item>> GetItemsAsync();
}

public class DataService : IDataService
{
    public async Task<List<Item>> GetItemsAsync() { /* ... */ }
}

// Register the interface → implementation mapping
builder.Services.AddSingleton<IDataService, DataService>();

In tests, substitute a mock without touching production code:

var services = new ServiceCollection();
services.AddSingleton<IDataService, FakeDataService>();

Source: SKILL.md on GitHub

No alerts16d4 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides comprehensive documentation and coding patterns for implementing Dependency Injection in .NET MAUI applications. It focuses on architecture, performance best practices, and avoiding common platform-specific pitfalls. No security threats or malicious patterns were identified.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    1 file scanned · No issues

Signed by skilld at 542317e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 6 months ago

README badge

README badge for davidortinau/maui-skills/maui-dependency-injection