$npx -y skills add managedcode/dotnet-skills --skill maui-dependency-injectionGuidance for configuring dependency injection in .NET MAUI apps — service registration in MauiProgram.cs, lifetime selection (Singleton / Transient / Scoped), constructor injection, Shell navigation auto-resolution, platform-specific registrations, and testability patterns. USE F
| 1 | # Dependency Injection in .NET MAUI |
| 2 | |
| 3 | .NET MAUI uses the same `Microsoft.Extensions.DependencyInjection` container as ASP.NET Core. All service registration happens in `MauiProgram.CreateMauiApp()` on `builder.Services`. The container is built once at startup and is immutable thereafter. |
| 4 | |
| 5 | ## When to Use |
| 6 | |
| 7 | - Registering services, ViewModels, and Pages in `MauiProgram.cs` |
| 8 | - Choosing between `AddSingleton`, `AddTransient`, and `AddScoped` |
| 9 | - Wiring constructor injection for Pages and ViewModels |
| 10 | - Leveraging Shell navigation to auto-resolve DI-registered Pages |
| 11 | - Registering platform-specific service implementations with `#if` directives |
| 12 | - Designing interfaces for testable service layers |
| 13 | |
| 14 | ## When Not to Use |
| 15 | |
| 16 | - XAML data-binding syntax or compiled bindings — use the **maui-data-binding** skill |
| 17 | - Shell route registration and query parameters — use the **maui-shell-navigation** skill |
| 18 | - Mocking frameworks or test runners — use standard .NET testing tools (xUnit, NUnit, MSTest) and mocking libraries (NSubstitute, Moq) |
| 19 | |
| 20 | ## Inputs |
| 21 | |
| 22 | - A .NET MAUI project with a `MauiProgram.cs` file |
| 23 | - Knowledge of which services, ViewModels, and Pages need registration |
| 24 | - Target platforms (Android, iOS, Mac Catalyst, Windows) for conditional registrations |
| 25 | |
| 26 | ## Workflow |
| 27 | |
| 28 | 1. Identify all services, ViewModels, and Pages that need to participate in dependency injection. |
| 29 | 2. Choose the correct lifetime for each type — `AddSingleton` for shared services, `AddTransient` for Pages and ViewModels. |
| 30 | 3. Register all types in `MauiProgram.CreateMauiApp()` on `builder.Services`, grouping by category (services, HTTP, ViewModels, Pages). |
| 31 | 4. Register Pages as Shell routes in `AppShell.xaml.cs` so Shell navigation auto-resolves the full dependency graph. |
| 32 | 5. Wire each Page to its ViewModel via constructor injection, assigning the ViewModel as `BindingContext`. |
| 33 | 6. Add platform-specific registrations with `#if` directives, ensuring every target platform is covered or has a fallback. |
| 34 | 7. Verify resolution works by running the app and confirming no `null` dependencies or missing-registration exceptions at runtime. |
| 35 | |
| 36 | --- |
| 37 | |
| 38 | ## Lifetime Selection |
| 39 | |
| 40 | | Lifetime | When to Use | Typical Types | |
| 41 | |---|---|---| |
| 42 | | `AddSingleton<T>()` | Shared state, expensive to create, app-wide config | `HttpClient` factory, settings service, database connection | |
| 43 | | `AddTransient<T>()` | Lightweight, stateless, or needs a fresh instance per use | Pages, ViewModels, per-call API wrappers | |
| 44 | | `AddScoped<T>()` | Per-scope lifetime with manually created `IServiceScope` | Scoped unit-of-work (rare in MAUI) | |
| 45 | |
| 46 | **Key rule:** Register Pages and ViewModels as **Transient**. Register shared services as **Singleton**. |
| 47 | |
| 48 | > ⚠️ **Avoid `AddScoped` unless you manually manage `IServiceScope`.** MAUI has no built-in request scope like ASP.NET Core. A Scoped registration without an explicit scope silently behaves as a Singleton, leading to subtle bugs. |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | ## Registration Pattern in MauiProgram.cs |
| 53 | |
| 54 | ```csharp |
| 55 | public static MauiApp CreateMauiApp() |
| 56 | { |
| 57 | var builder = MauiApp.CreateBuilder(); |
| 58 | builder.UseMauiApp<App>(); |
| 59 | |
| 60 | // Services — Singleton for shared state |
| 61 | builder.Services.AddSingleton<IDataService, DataService>(); |
| 62 | builder.Services.AddSingleton<ISettingsService, SettingsService>(); |
| 63 | |
| 64 | // HTTP — use typed or named clients via IHttpClientFactory |
| 65 | // Requires NuGet: Microsoft.Extensions.Http |
| 66 | builder.Services.AddHttpClient<IApiClient, ApiClient>(); |
| 67 | |
| 68 | // ViewModels — Transient for fresh state per navigation |
| 69 | builder.Services.AddTransient<MainViewModel>(); |
| 70 | builder.Services.AddTransient<DetailViewModel>(); |
| 71 | |
| 72 | // Pages — Transient so constructor injection fires each time |
| 73 | builder.Services.AddTransient<MainPage>(); |
| 74 | builder.Services.AddTransient<DetailPage>(); |
| 75 | |
| 76 | return builder.Build(); |
| 77 | } |
| 78 | ``` |
| 79 | |
| 80 | --- |
| 81 | |
| 82 | ## Constructor Injection |
| 83 | |
| 84 | Inject dependencies through constructor parameters. The container resolves them automatically when the type is itself resolved from DI. |
| 85 | |
| 86 | ```csharp |
| 87 | public class MainViewModel |
| 88 | { |
| 89 | private readonly IDataService _dataService; |
| 90 | |
| 91 | public MainViewModel(IDataService dataService) |
| 92 | { |
| 93 | _dataService = dataService; |
| 94 | } |
| 95 | |
| 96 | public async Task LoadAsync() => Items = await _dataService.GetItemsAs |