[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-dotnet-maui-dependency-injection":3,"mdc--7uu72n-key":37,"related-repo-dotnet-maui-dependency-injection":2450,"related-org-dotnet-maui-dependency-injection":2560},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":25,"repoUrl":26,"updatedAt":27,"license":28,"forks":29,"topics":30,"repo":32,"sourceUrl":35,"mdContent":36},"maui-dependency-injection","configure dependency injection in .NET MAUI apps","Guidance for configuring dependency injection in .NET MAUI apps — service registration in MauiProgram.cs, lifetime selection (Singleton \u002F Transient \u002F Scoped), constructor injection, Shell navigation auto-resolution, platform-specific registrations, and testability patterns. USE FOR: \"dependency injection\", \"DI setup\", \"AddSingleton\", \"AddTransient\", \"AddScoped\", \"service registration\", \"constructor injection\", \"IServiceProvider\", \"MauiProgram DI\", \"register services\", \"BindingContext injection\". DO NOT USE FOR: data binding (use maui-data-binding), Shell route configuration (use maui-shell-navigation), unit-test mocking frameworks (use standard xUnit and NSubstitute patterns).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"dotnet",".NET (Microsoft)","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fdotnet.png",[12,16,19,22],{"name":13,"slug":14,"type":15},"Architecture","architecture","tag",{"name":17,"slug":18,"type":15},"MAUI","maui",{"name":20,"slug":21,"type":15},".NET","net",{"name":23,"slug":24,"type":15},"Mobile","mobile",4576,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills","2026-08-01T05:42:22.181112","MIT",332,[31],"agent-skills",{"repoUrl":26,"stars":25,"forks":29,"topics":33,"description":34},[31],"Repository for skills to assist AI coding agents with .NET and C#","https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fdotnet-maui\u002Fskills\u002Fmaui-dependency-injection","---\nname: maui-dependency-injection\ndescription: >\n  Guidance for configuring dependency injection in .NET MAUI apps — service\n  registration in MauiProgram.cs, lifetime selection (Singleton \u002F Transient \u002F Scoped),\n  constructor injection, Shell navigation auto-resolution, platform-specific\n  registrations, and testability patterns.\n  USE FOR: \"dependency injection\", \"DI setup\", \"AddSingleton\", \"AddTransient\",\n  \"AddScoped\", \"service registration\", \"constructor injection\", \"IServiceProvider\",\n  \"MauiProgram DI\", \"register services\", \"BindingContext injection\".\n  DO NOT USE FOR: data binding (use maui-data-binding), Shell route configuration\n  (use maui-shell-navigation), unit-test mocking frameworks (use standard xUnit\n  and NSubstitute patterns).\nlicense: MIT\n---\n\n# Dependency Injection in .NET MAUI\n\n.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.\n\n## When to Use\n\n- Registering services, ViewModels, and Pages in `MauiProgram.cs`\n- Choosing between `AddSingleton`, `AddTransient`, and `AddScoped`\n- Wiring constructor injection for Pages and ViewModels\n- Leveraging Shell navigation to auto-resolve DI-registered Pages\n- Registering platform-specific service implementations with `#if` directives\n- Designing interfaces for testable service layers\n\n## When Not to Use\n\n- XAML data-binding syntax or compiled bindings — use the **maui-data-binding** skill\n- Shell route registration and query parameters — use the **maui-shell-navigation** skill\n- Mocking frameworks or test runners — use standard .NET testing tools (xUnit, NUnit, MSTest) and mocking libraries (NSubstitute, Moq)\n\n## Inputs\n\n- A .NET MAUI project with a `MauiProgram.cs` file\n- Knowledge of which services, ViewModels, and Pages need registration\n- Target platforms (Android, iOS, Mac Catalyst, Windows) for conditional registrations\n\n## Rules That Change the Answer\n\n| Situation | Do this | Why |\n|---|---|---|\n| Registering a Page or ViewModel | Prefer `AddTransient` | A fresh instance per navigation avoids stale state, and a Singleton page cannot be re-added to the visual tree after it is removed. Singleton is defensible for a genuinely single-instance page (e.g. a root tab you want to keep warm) |\n| Registering shared\u002Fexpensive state | `AddSingleton` | One instance app-wide (settings, DB connection, `HttpClient` handler) |\n| Tempted to use `AddScoped` | Use `AddTransient` (or `AddSingleton` if sharing is intended) | MAUI has **no** built-in request scope like ASP.NET Core's HTTP pipeline. MAUI does create one `IServiceScope` per window, so a Scoped service lives as long as that window — and resolved from the root provider it behaves like a Singleton. Neither gives you per-navigation freshness |\n| Navigating to a DI-registered page | Register the page **and** its ViewModel, then `Routing.RegisterRoute` | `Shell.Current.GoToAsync` resolves the page through DI and injects its constructor dependencies |\n| Platform-specific implementation | `#if` per platform **with every platform covered** | A missing platform branch leaves the service unregistered and throws at resolution time |\n\n**Do not** introduce DI into a project that isn't using it, swap a working service\nlifetime, or add an interface purely for symmetry — only when the user asked or it\nfixes a real defect.\n\n**Answer narrowly, but completely.** When you recommend a lifetime change, show the\nregistration code, and give the realistic alternatives rather than a single verdict —\nfor a unit-of-work or `DbContext` question that means `AddTransient`, an explicit\n`IServiceScopeFactory.CreateScope()`, **and** the factory pattern\n(`AddDbContextFactory`), with a note on when each fits. A one-line prescription is\nusually a worse answer than a short menu with trade-offs.\n\n```csharp\n\u002F\u002F Explicit scope when you genuinely need unit-of-work semantics\nusing var scope = scopeFactory.CreateScope();\nvar db = scope.ServiceProvider.GetRequiredService\u003CMyDbContext>();\n```\n\n## Workflow\n\n1. Identify all services, ViewModels, and Pages that need to participate in dependency injection.\n2. Choose the correct lifetime for each type — `AddSingleton` for shared services, `AddTransient` for Pages and ViewModels.\n3. Register all types in `MauiProgram.CreateMauiApp()` on `builder.Services`, grouping by category (services, HTTP, ViewModels, Pages).\n4. Register Pages as Shell routes in `AppShell.xaml.cs` so Shell navigation auto-resolves the full dependency graph.\n5. Wire each Page to its ViewModel via constructor injection, assigning the ViewModel as `BindingContext`.\n6. Add platform-specific registrations with `#if` directives, ensuring every target platform is covered or has a fallback.\n7. Verify resolution works by running the app and confirming no `null` dependencies or missing-registration exceptions at runtime.\n\n---\n\n## Lifetime Selection\n\n| Lifetime | When to Use | Typical Types |\n|---|---|---|\n| `AddSingleton\u003CT>()` | Shared state, expensive to create, app-wide config | `HttpClient` factory, settings service, database connection |\n| `AddTransient\u003CT>()` | Lightweight, stateless, or needs a fresh instance per use | Pages, ViewModels, per-call API wrappers |\n| `AddScoped\u003CT>()` | Per-window lifetime, or a manually created `IServiceScope` | Scoped unit-of-work (rare in MAUI) |\n\n**Key rule:** Register Pages and ViewModels as **Transient** by default. Register shared services as **Singleton**.\n\n> ⚠️ **Avoid `AddScoped` unless you manually manage `IServiceScope`.** MAUI has no built-in request scope like ASP.NET Core. MAUI creates one `IServiceScope` per window, so a Scoped service lives as long as that window; resolved from the root provider it silently behaves as a Singleton. Neither gives per-navigation freshness.\n\n---\n\n## Registration Pattern in MauiProgram.cs\n\n```csharp\npublic static MauiApp CreateMauiApp()\n{\n    var builder = MauiApp.CreateBuilder();\n    builder.UseMauiApp\u003CApp>();\n\n    \u002F\u002F Services — Singleton for shared state\n    builder.Services.AddSingleton\u003CIDataService, DataService>();\n    builder.Services.AddSingleton\u003CISettingsService, SettingsService>();\n\n    \u002F\u002F HTTP — use typed or named clients via IHttpClientFactory\n    \u002F\u002F Requires NuGet: Microsoft.Extensions.Http\n    builder.Services.AddHttpClient\u003CIApiClient, ApiClient>();\n\n    \u002F\u002F ViewModels — Transient for fresh state per navigation\n    builder.Services.AddTransient\u003CMainViewModel>();\n    builder.Services.AddTransient\u003CDetailViewModel>();\n\n    \u002F\u002F Pages — Transient so constructor injection fires each time\n    builder.Services.AddTransient\u003CMainPage>();\n    builder.Services.AddTransient\u003CDetailPage>();\n\n    return builder.Build();\n}\n```\n\n---\n\n## Constructor Injection\n\nInject dependencies through constructor parameters. The container resolves them automatically when the type is itself resolved from DI.\n\n```csharp\npublic class MainViewModel\n{\n    private readonly IDataService _dataService;\n\n    public MainViewModel(IDataService dataService)\n    {\n        _dataService = dataService;\n    }\n\n    public async Task LoadAsync() => Items = await _dataService.GetItemsAsync();\n}\n```\n\n### ViewModel → Page Wiring\n\nRegister both Page and ViewModel. Inject the ViewModel into the Page and assign it as `BindingContext`:\n\n```csharp\npublic partial class MainPage : ContentPage\n{\n    public MainPage(MainViewModel viewModel)\n    {\n        InitializeComponent();\n        BindingContext = viewModel;\n    }\n}\n```\n\n---\n\n## Shell Navigation Auto-Resolution\n\nWhen a Page is registered in DI **and** as a Shell route, Shell resolves it (and its full dependency graph) automatically on navigation:\n\n```csharp\n\u002F\u002F MauiProgram.cs\nbuilder.Services.AddTransient\u003CDetailPage>();\nbuilder.Services.AddTransient\u003CDetailViewModel>();\n\n\u002F\u002F AppShell.xaml.cs\nRouting.RegisterRoute(nameof(DetailPage), typeof(DetailPage));\n\n\u002F\u002F Navigate — DI resolves DetailPage + DetailViewModel\nawait Shell.Current.GoToAsync(nameof(DetailPage));\n```\n\n### Passing parameters to a DI-resolved ViewModel\n\nDI supplies the ViewModel's *dependencies*; navigation parameters arrive separately.\nDon't try to inject them through the constructor — implement `IQueryAttributable`\non the ViewModel so it receives both:\n\n```csharp\npublic class DetailViewModel : ObservableObject, IQueryAttributable\n{\n    readonly IDataService _data;   \u002F\u002F ← injected by DI\n\n    public DetailViewModel(IDataService data) => _data = data;\n\n    public void ApplyQueryAttributes(IDictionary\u003Cstring, object> query)\n    {\n        \u002F\u002F ← supplied by navigation\n        if (query.TryGetValue(\"id\", out var id))\n            LoadAsync(id.ToString()!);\n    }\n}\n\n\u002F\u002F Navigate with a parameter — the page and its ViewModel still come from DI\nawait Shell.Current.GoToAsync($\"{nameof(DetailPage)}?id={product.Id}\");\n```\n\nShell applies query attributes to the page **and** its `BindingContext`, so the\nViewModel receives them without any wiring in the page.\n\n---\n\n## Platform-Specific Registration\n\nUse preprocessor directives to register platform implementations. Always cover every target platform or provide a no-op fallback to avoid runtime `null`.\n\n```csharp\n#if ANDROID\nbuilder.Services.AddSingleton\u003CINotificationService, AndroidNotificationService>();\n#elif IOS || MACCATALYST\nbuilder.Services.AddSingleton\u003CINotificationService, AppleNotificationService>();\n#elif WINDOWS\nbuilder.Services.AddSingleton\u003CINotificationService, WindowsNotificationService>();\n#else\nbuilder.Services.AddSingleton\u003CINotificationService, NoOpNotificationService>();\n#endif\n```\n\n---\n\n## Explicit Resolution (Last Resort)\n\nPrefer constructor injection. Use explicit resolution only where injection is genuinely unavailable (custom handlers, platform callbacks):\n\n```csharp\n\u002F\u002F From any Element with a Handler\nvar service = this.Handler.MauiContext.Services.GetService\u003CIDataService>();\n```\n\nFor dynamic resolution, inject `IServiceProvider`:\n\n```csharp\npublic class NavigationService(IServiceProvider serviceProvider)\n{\n    public T ResolvePage\u003CT>() where T : Page\n        => serviceProvider.GetRequiredService\u003CT>();\n}\n```\n\n---\n\n## Interface-First Pattern for Testability\n\nDefine interfaces for every service so implementations can be swapped in tests:\n\n```csharp\npublic interface IDataService\n{\n    Task\u003CList\u003CItem>> GetItemsAsync();\n}\n\n\u002F\u002F Production registration\nbuilder.Services.AddSingleton\u003CIDataService, DataService>();\n\n\u002F\u002F Test registration — swap without touching production code\nvar services = new ServiceCollection();\nservices.AddSingleton\u003CIDataService, FakeDataService>();\n```\n\n---\n\n## Common Pitfalls\n\n### 1. Singleton ViewModels Cause Stale Data\n\n```csharp\n\u002F\u002F ❌ ViewModel keeps stale state across navigations\nbuilder.Services.AddSingleton\u003CDetailViewModel>();\n\n\u002F\u002F ✅ Fresh instance each navigation\nbuilder.Services.AddTransient\u003CDetailViewModel>();\n```\n\n### 2. ContentTemplate Pages Are Not Created Through DI\n\nPages declared in Shell XAML via `\u003CShellContent ContentTemplate=\"{DataTemplate views:DetailPage}\">` are instantiated with `Activator.CreateInstance` (`ElementTemplate.cs`), **not** through the service provider. Constructor injection does not run on that path: if the page's only constructor takes dependencies, you get a `MissingMethodException` — not a silently `null` dependency.\n\nPages reached through `Routing.RegisterRoute` + `GoToAsync` are different: they go through `ActivatorUtilities.GetServiceOrCreateInstance` (`Routing.cs`), which injects registered dependencies even if the page type itself was never registered, and **throws** if a required dependency cannot be resolved.\n\n```csharp\n\u002F\u002F Registering the page and its dependencies keeps both paths working\nbuilder.Services.AddTransient\u003CDetailPage>();\nbuilder.Services.AddTransient\u003CDetailViewModel>();\n```\n\nIf you need DI for a tab\u002Fflyout page, give it a parameterless constructor that resolves what it needs, or navigate to it by route instead of embedding it in `ContentTemplate`.\n\n### 3. XAML Resource Parsing vs. DI Timing\n\nXAML resources in `App.xaml` are parsed during `InitializeComponent()` — before the container is fully available. Defer service-dependent work to `CreateWindow()`:\n\n```csharp\npublic partial class App : Application\n{\n    private readonly IServiceProvider _services;\n\n    public App(IServiceProvider services)\n    {\n        _services = services;\n        InitializeComponent();\n    }\n\n    protected override Window CreateWindow(IActivationState? activationState)\n    {\n        \u002F\u002F Safe — container is fully built\n        \u002F\u002F Requires: builder.Services.AddTransient\u003CAppShell>() in MauiProgram.cs\n        var appShell = _services.GetRequiredService\u003CAppShell>();\n        return new Window(appShell);\n    }\n}\n```\n\n### 4. Service Locator Anti-Pattern\n\n```csharp\n\u002F\u002F ❌ Hides dependencies, hard to test\nvar svc = this.Handler.MauiContext.Services.GetService\u003CIDataService>();\n\n\u002F\u002F ✅ Constructor injection — explicit and testable\npublic class MyViewModel(IDataService dataService) { }\n```\n\n### 5. Missing Platform in Conditional Registration\n\nForgetting a platform in `#if` blocks means `GetService\u003CT>()` returns `null` at runtime on that platform. Always include an `#else` fallback or cover every target.\n\n### 6. AddScoped Without Manual Scope\n\nSee the rule table above: `AddScoped` gives you either window lifetime or Singleton behaviour, never per-navigation freshness. Use `AddTransient` or `AddSingleton` unless you explicitly create and manage an `IServiceScope`.\n\n---\n\n## Checklist\n\n- [ ] Every Page and ViewModel that needs injection is registered in `MauiProgram.cs`\n- [ ] Pages and ViewModels use `AddTransient`; shared services use `AddSingleton`\n- [ ] Constructor injection used everywhere possible; service locator only as last resort\n- [ ] Interfaces defined for services that need test substitution\n- [ ] Platform-specific `#if` registrations cover all target platforms or include a fallback\n- [ ] Service-dependent work deferred to `CreateWindow()`, not run during XAML parse\n- [ ] `AddScoped` used only when window lifetime is intended, or alongside a manually created `IServiceScope`\n\n## References\n\n- [Dependency injection in .NET MAUI](https:\u002F\u002Flearn.microsoft.com\u002Fdotnet\u002Fmaui\u002Ffundamentals\u002Fdependency-injection)\n- [.NET dependency injection fundamentals](https:\u002F\u002Flearn.microsoft.com\u002Fdotnet\u002Fcore\u002Fextensions\u002Fdependency-injection)\n",{"data":38,"body":39},{"name":4,"description":6,"license":28},{"type":40,"children":41},"root",[42,51,82,89,160,166,198,204,229,235,437,447,493,533,539,636,640,646,748,771,807,810,816,1024,1027,1033,1038,1129,1136,1148,1215,1218,1224,1235,1312,1318,1339,1467,1485,1488,1494,1505,1584,1587,1593,1598,1621,1633,1678,1681,1687,1692,1783,1786,1792,1798,1843,1849,1900,1942,1971,1983,1989,2017,2159,2165,2211,2217,2252,2258,2290,2293,2299,2413,2419,2444],{"type":43,"tag":44,"props":45,"children":47},"element","h1",{"id":46},"dependency-injection-in-net-maui",[48],{"type":49,"value":50},"text","Dependency Injection in .NET MAUI",{"type":43,"tag":52,"props":53,"children":54},"p",{},[55,57,64,66,72,74,80],{"type":49,"value":56},".NET MAUI uses the same ",{"type":43,"tag":58,"props":59,"children":61},"code",{"className":60},[],[62],{"type":49,"value":63},"Microsoft.Extensions.DependencyInjection",{"type":49,"value":65}," container as ASP.NET Core. All service registration happens in ",{"type":43,"tag":58,"props":67,"children":69},{"className":68},[],[70],{"type":49,"value":71},"MauiProgram.CreateMauiApp()",{"type":49,"value":73}," on ",{"type":43,"tag":58,"props":75,"children":77},{"className":76},[],[78],{"type":49,"value":79},"builder.Services",{"type":49,"value":81},". The container is built once at startup and is immutable thereafter.",{"type":43,"tag":83,"props":84,"children":86},"h2",{"id":85},"when-to-use",[87],{"type":49,"value":88},"When to Use",{"type":43,"tag":90,"props":91,"children":92},"ul",{},[93,105,132,137,142,155],{"type":43,"tag":94,"props":95,"children":96},"li",{},[97,99],{"type":49,"value":98},"Registering services, ViewModels, and Pages in ",{"type":43,"tag":58,"props":100,"children":102},{"className":101},[],[103],{"type":49,"value":104},"MauiProgram.cs",{"type":43,"tag":94,"props":106,"children":107},{},[108,110,116,118,124,126],{"type":49,"value":109},"Choosing between ",{"type":43,"tag":58,"props":111,"children":113},{"className":112},[],[114],{"type":49,"value":115},"AddSingleton",{"type":49,"value":117},", ",{"type":43,"tag":58,"props":119,"children":121},{"className":120},[],[122],{"type":49,"value":123},"AddTransient",{"type":49,"value":125},", and ",{"type":43,"tag":58,"props":127,"children":129},{"className":128},[],[130],{"type":49,"value":131},"AddScoped",{"type":43,"tag":94,"props":133,"children":134},{},[135],{"type":49,"value":136},"Wiring constructor injection for Pages and ViewModels",{"type":43,"tag":94,"props":138,"children":139},{},[140],{"type":49,"value":141},"Leveraging Shell navigation to auto-resolve DI-registered Pages",{"type":43,"tag":94,"props":143,"children":144},{},[145,147,153],{"type":49,"value":146},"Registering platform-specific service implementations with ",{"type":43,"tag":58,"props":148,"children":150},{"className":149},[],[151],{"type":49,"value":152},"#if",{"type":49,"value":154}," directives",{"type":43,"tag":94,"props":156,"children":157},{},[158],{"type":49,"value":159},"Designing interfaces for testable service layers",{"type":43,"tag":83,"props":161,"children":163},{"id":162},"when-not-to-use",[164],{"type":49,"value":165},"When Not to Use",{"type":43,"tag":90,"props":167,"children":168},{},[169,182,193],{"type":43,"tag":94,"props":170,"children":171},{},[172,174,180],{"type":49,"value":173},"XAML data-binding syntax or compiled bindings — use the ",{"type":43,"tag":175,"props":176,"children":177},"strong",{},[178],{"type":49,"value":179},"maui-data-binding",{"type":49,"value":181}," skill",{"type":43,"tag":94,"props":183,"children":184},{},[185,187,192],{"type":49,"value":186},"Shell route registration and query parameters — use the ",{"type":43,"tag":175,"props":188,"children":189},{},[190],{"type":49,"value":191},"maui-shell-navigation",{"type":49,"value":181},{"type":43,"tag":94,"props":194,"children":195},{},[196],{"type":49,"value":197},"Mocking frameworks or test runners — use standard .NET testing tools (xUnit, NUnit, MSTest) and mocking libraries (NSubstitute, Moq)",{"type":43,"tag":83,"props":199,"children":201},{"id":200},"inputs",[202],{"type":49,"value":203},"Inputs",{"type":43,"tag":90,"props":205,"children":206},{},[207,219,224],{"type":43,"tag":94,"props":208,"children":209},{},[210,212,217],{"type":49,"value":211},"A .NET MAUI project with a ",{"type":43,"tag":58,"props":213,"children":215},{"className":214},[],[216],{"type":49,"value":104},{"type":49,"value":218}," file",{"type":43,"tag":94,"props":220,"children":221},{},[222],{"type":49,"value":223},"Knowledge of which services, ViewModels, and Pages need registration",{"type":43,"tag":94,"props":225,"children":226},{},[227],{"type":49,"value":228},"Target platforms (Android, iOS, Mac Catalyst, Windows) for conditional registrations",{"type":43,"tag":83,"props":230,"children":232},{"id":231},"rules-that-change-the-answer",[233],{"type":49,"value":234},"Rules That Change the Answer",{"type":43,"tag":236,"props":237,"children":238},"table",{},[239,263],{"type":43,"tag":240,"props":241,"children":242},"thead",{},[243],{"type":43,"tag":244,"props":245,"children":246},"tr",{},[247,253,258],{"type":43,"tag":248,"props":249,"children":250},"th",{},[251],{"type":49,"value":252},"Situation",{"type":43,"tag":248,"props":254,"children":255},{},[256],{"type":49,"value":257},"Do this",{"type":43,"tag":248,"props":259,"children":260},{},[261],{"type":49,"value":262},"Why",{"type":43,"tag":264,"props":265,"children":266},"tbody",{},[267,291,320,372,409],{"type":43,"tag":244,"props":268,"children":269},{},[270,276,286],{"type":43,"tag":271,"props":272,"children":273},"td",{},[274],{"type":49,"value":275},"Registering a Page or ViewModel",{"type":43,"tag":271,"props":277,"children":278},{},[279,281],{"type":49,"value":280},"Prefer ",{"type":43,"tag":58,"props":282,"children":284},{"className":283},[],[285],{"type":49,"value":123},{"type":43,"tag":271,"props":287,"children":288},{},[289],{"type":49,"value":290},"A fresh instance per navigation avoids stale state, and a Singleton page cannot be re-added to the visual tree after it is removed. Singleton is defensible for a genuinely single-instance page (e.g. a root tab you want to keep warm)",{"type":43,"tag":244,"props":292,"children":293},{},[294,299,307],{"type":43,"tag":271,"props":295,"children":296},{},[297],{"type":49,"value":298},"Registering shared\u002Fexpensive state",{"type":43,"tag":271,"props":300,"children":301},{},[302],{"type":43,"tag":58,"props":303,"children":305},{"className":304},[],[306],{"type":49,"value":115},{"type":43,"tag":271,"props":308,"children":309},{},[310,312,318],{"type":49,"value":311},"One instance app-wide (settings, DB connection, ",{"type":43,"tag":58,"props":313,"children":315},{"className":314},[],[316],{"type":49,"value":317},"HttpClient",{"type":49,"value":319}," handler)",{"type":43,"tag":244,"props":321,"children":322},{},[323,333,352],{"type":43,"tag":271,"props":324,"children":325},{},[326,328],{"type":49,"value":327},"Tempted to use ",{"type":43,"tag":58,"props":329,"children":331},{"className":330},[],[332],{"type":49,"value":131},{"type":43,"tag":271,"props":334,"children":335},{},[336,338,343,345,350],{"type":49,"value":337},"Use ",{"type":43,"tag":58,"props":339,"children":341},{"className":340},[],[342],{"type":49,"value":123},{"type":49,"value":344}," (or ",{"type":43,"tag":58,"props":346,"children":348},{"className":347},[],[349],{"type":49,"value":115},{"type":49,"value":351}," if sharing is intended)",{"type":43,"tag":271,"props":353,"children":354},{},[355,357,362,364,370],{"type":49,"value":356},"MAUI has ",{"type":43,"tag":175,"props":358,"children":359},{},[360],{"type":49,"value":361},"no",{"type":49,"value":363}," built-in request scope like ASP.NET Core's HTTP pipeline. MAUI does create one ",{"type":43,"tag":58,"props":365,"children":367},{"className":366},[],[368],{"type":49,"value":369},"IServiceScope",{"type":49,"value":371}," per window, so a Scoped service lives as long as that window — and resolved from the root provider it behaves like a Singleton. Neither gives you per-navigation freshness",{"type":43,"tag":244,"props":373,"children":374},{},[375,380,398],{"type":43,"tag":271,"props":376,"children":377},{},[378],{"type":49,"value":379},"Navigating to a DI-registered page",{"type":43,"tag":271,"props":381,"children":382},{},[383,385,390,392],{"type":49,"value":384},"Register the page ",{"type":43,"tag":175,"props":386,"children":387},{},[388],{"type":49,"value":389},"and",{"type":49,"value":391}," its ViewModel, then ",{"type":43,"tag":58,"props":393,"children":395},{"className":394},[],[396],{"type":49,"value":397},"Routing.RegisterRoute",{"type":43,"tag":271,"props":399,"children":400},{},[401,407],{"type":43,"tag":58,"props":402,"children":404},{"className":403},[],[405],{"type":49,"value":406},"Shell.Current.GoToAsync",{"type":49,"value":408}," resolves the page through DI and injects its constructor dependencies",{"type":43,"tag":244,"props":410,"children":411},{},[412,417,432],{"type":43,"tag":271,"props":413,"children":414},{},[415],{"type":49,"value":416},"Platform-specific implementation",{"type":43,"tag":271,"props":418,"children":419},{},[420,425,427],{"type":43,"tag":58,"props":421,"children":423},{"className":422},[],[424],{"type":49,"value":152},{"type":49,"value":426}," per platform ",{"type":43,"tag":175,"props":428,"children":429},{},[430],{"type":49,"value":431},"with every platform covered",{"type":43,"tag":271,"props":433,"children":434},{},[435],{"type":49,"value":436},"A missing platform branch leaves the service unregistered and throws at resolution time",{"type":43,"tag":52,"props":438,"children":439},{},[440,445],{"type":43,"tag":175,"props":441,"children":442},{},[443],{"type":49,"value":444},"Do not",{"type":49,"value":446}," introduce DI into a project that isn't using it, swap a working service\nlifetime, or add an interface purely for symmetry — only when the user asked or it\nfixes a real defect.",{"type":43,"tag":52,"props":448,"children":449},{},[450,455,457,463,465,470,472,478,479,483,485,491],{"type":43,"tag":175,"props":451,"children":452},{},[453],{"type":49,"value":454},"Answer narrowly, but completely.",{"type":49,"value":456}," When you recommend a lifetime change, show the\nregistration code, and give the realistic alternatives rather than a single verdict —\nfor a unit-of-work or ",{"type":43,"tag":58,"props":458,"children":460},{"className":459},[],[461],{"type":49,"value":462},"DbContext",{"type":49,"value":464}," question that means ",{"type":43,"tag":58,"props":466,"children":468},{"className":467},[],[469],{"type":49,"value":123},{"type":49,"value":471},", an explicit\n",{"type":43,"tag":58,"props":473,"children":475},{"className":474},[],[476],{"type":49,"value":477},"IServiceScopeFactory.CreateScope()",{"type":49,"value":117},{"type":43,"tag":175,"props":480,"children":481},{},[482],{"type":49,"value":389},{"type":49,"value":484}," the factory pattern\n(",{"type":43,"tag":58,"props":486,"children":488},{"className":487},[],[489],{"type":49,"value":490},"AddDbContextFactory",{"type":49,"value":492},"), with a note on when each fits. A one-line prescription is\nusually a worse answer than a short menu with trade-offs.",{"type":43,"tag":494,"props":495,"children":500},"pre",{"className":496,"code":497,"language":498,"meta":499,"style":499},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F Explicit scope when you genuinely need unit-of-work semantics\nusing var scope = scopeFactory.CreateScope();\nvar db = scope.ServiceProvider.GetRequiredService\u003CMyDbContext>();\n","csharp","",[501],{"type":43,"tag":58,"props":502,"children":503},{"__ignoreMap":499},[504,515,524],{"type":43,"tag":505,"props":506,"children":509},"span",{"class":507,"line":508},"line",1,[510],{"type":43,"tag":505,"props":511,"children":512},{},[513],{"type":49,"value":514},"\u002F\u002F Explicit scope when you genuinely need unit-of-work semantics\n",{"type":43,"tag":505,"props":516,"children":518},{"class":507,"line":517},2,[519],{"type":43,"tag":505,"props":520,"children":521},{},[522],{"type":49,"value":523},"using var scope = scopeFactory.CreateScope();\n",{"type":43,"tag":505,"props":525,"children":527},{"class":507,"line":526},3,[528],{"type":43,"tag":505,"props":529,"children":530},{},[531],{"type":49,"value":532},"var db = scope.ServiceProvider.GetRequiredService\u003CMyDbContext>();\n",{"type":43,"tag":83,"props":534,"children":536},{"id":535},"workflow",[537],{"type":49,"value":538},"Workflow",{"type":43,"tag":540,"props":541,"children":542},"ol",{},[543,548,567,585,598,611,623],{"type":43,"tag":94,"props":544,"children":545},{},[546],{"type":49,"value":547},"Identify all services, ViewModels, and Pages that need to participate in dependency injection.",{"type":43,"tag":94,"props":549,"children":550},{},[551,553,558,560,565],{"type":49,"value":552},"Choose the correct lifetime for each type — ",{"type":43,"tag":58,"props":554,"children":556},{"className":555},[],[557],{"type":49,"value":115},{"type":49,"value":559}," for shared services, ",{"type":43,"tag":58,"props":561,"children":563},{"className":562},[],[564],{"type":49,"value":123},{"type":49,"value":566}," for Pages and ViewModels.",{"type":43,"tag":94,"props":568,"children":569},{},[570,572,577,578,583],{"type":49,"value":571},"Register all types in ",{"type":43,"tag":58,"props":573,"children":575},{"className":574},[],[576],{"type":49,"value":71},{"type":49,"value":73},{"type":43,"tag":58,"props":579,"children":581},{"className":580},[],[582],{"type":49,"value":79},{"type":49,"value":584},", grouping by category (services, HTTP, ViewModels, Pages).",{"type":43,"tag":94,"props":586,"children":587},{},[588,590,596],{"type":49,"value":589},"Register Pages as Shell routes in ",{"type":43,"tag":58,"props":591,"children":593},{"className":592},[],[594],{"type":49,"value":595},"AppShell.xaml.cs",{"type":49,"value":597}," so Shell navigation auto-resolves the full dependency graph.",{"type":43,"tag":94,"props":599,"children":600},{},[601,603,609],{"type":49,"value":602},"Wire each Page to its ViewModel via constructor injection, assigning the ViewModel as ",{"type":43,"tag":58,"props":604,"children":606},{"className":605},[],[607],{"type":49,"value":608},"BindingContext",{"type":49,"value":610},".",{"type":43,"tag":94,"props":612,"children":613},{},[614,616,621],{"type":49,"value":615},"Add platform-specific registrations with ",{"type":43,"tag":58,"props":617,"children":619},{"className":618},[],[620],{"type":49,"value":152},{"type":49,"value":622}," directives, ensuring every target platform is covered or has a fallback.",{"type":43,"tag":94,"props":624,"children":625},{},[626,628,634],{"type":49,"value":627},"Verify resolution works by running the app and confirming no ",{"type":43,"tag":58,"props":629,"children":631},{"className":630},[],[632],{"type":49,"value":633},"null",{"type":49,"value":635}," dependencies or missing-registration exceptions at runtime.",{"type":43,"tag":637,"props":638,"children":639},"hr",{},[],{"type":43,"tag":83,"props":641,"children":643},{"id":642},"lifetime-selection",[644],{"type":49,"value":645},"Lifetime Selection",{"type":43,"tag":236,"props":647,"children":648},{},[649,669],{"type":43,"tag":240,"props":650,"children":651},{},[652],{"type":43,"tag":244,"props":653,"children":654},{},[655,660,664],{"type":43,"tag":248,"props":656,"children":657},{},[658],{"type":49,"value":659},"Lifetime",{"type":43,"tag":248,"props":661,"children":662},{},[663],{"type":49,"value":88},{"type":43,"tag":248,"props":665,"children":666},{},[667],{"type":49,"value":668},"Typical Types",{"type":43,"tag":264,"props":670,"children":671},{},[672,699,721],{"type":43,"tag":244,"props":673,"children":674},{},[675,684,689],{"type":43,"tag":271,"props":676,"children":677},{},[678],{"type":43,"tag":58,"props":679,"children":681},{"className":680},[],[682],{"type":49,"value":683},"AddSingleton\u003CT>()",{"type":43,"tag":271,"props":685,"children":686},{},[687],{"type":49,"value":688},"Shared state, expensive to create, app-wide config",{"type":43,"tag":271,"props":690,"children":691},{},[692,697],{"type":43,"tag":58,"props":693,"children":695},{"className":694},[],[696],{"type":49,"value":317},{"type":49,"value":698}," factory, settings service, database connection",{"type":43,"tag":244,"props":700,"children":701},{},[702,711,716],{"type":43,"tag":271,"props":703,"children":704},{},[705],{"type":43,"tag":58,"props":706,"children":708},{"className":707},[],[709],{"type":49,"value":710},"AddTransient\u003CT>()",{"type":43,"tag":271,"props":712,"children":713},{},[714],{"type":49,"value":715},"Lightweight, stateless, or needs a fresh instance per use",{"type":43,"tag":271,"props":717,"children":718},{},[719],{"type":49,"value":720},"Pages, ViewModels, per-call API wrappers",{"type":43,"tag":244,"props":722,"children":723},{},[724,733,743],{"type":43,"tag":271,"props":725,"children":726},{},[727],{"type":43,"tag":58,"props":728,"children":730},{"className":729},[],[731],{"type":49,"value":732},"AddScoped\u003CT>()",{"type":43,"tag":271,"props":734,"children":735},{},[736,738],{"type":49,"value":737},"Per-window lifetime, or a manually created ",{"type":43,"tag":58,"props":739,"children":741},{"className":740},[],[742],{"type":49,"value":369},{"type":43,"tag":271,"props":744,"children":745},{},[746],{"type":49,"value":747},"Scoped unit-of-work (rare in MAUI)",{"type":43,"tag":52,"props":749,"children":750},{},[751,756,758,763,765,770],{"type":43,"tag":175,"props":752,"children":753},{},[754],{"type":49,"value":755},"Key rule:",{"type":49,"value":757}," Register Pages and ViewModels as ",{"type":43,"tag":175,"props":759,"children":760},{},[761],{"type":49,"value":762},"Transient",{"type":49,"value":764}," by default. Register shared services as ",{"type":43,"tag":175,"props":766,"children":767},{},[768],{"type":49,"value":769},"Singleton",{"type":49,"value":610},{"type":43,"tag":772,"props":773,"children":774},"blockquote",{},[775],{"type":43,"tag":52,"props":776,"children":777},{},[778,780,798,800,805],{"type":49,"value":779},"⚠️ ",{"type":43,"tag":175,"props":781,"children":782},{},[783,785,790,792,797],{"type":49,"value":784},"Avoid ",{"type":43,"tag":58,"props":786,"children":788},{"className":787},[],[789],{"type":49,"value":131},{"type":49,"value":791}," unless you manually manage ",{"type":43,"tag":58,"props":793,"children":795},{"className":794},[],[796],{"type":49,"value":369},{"type":49,"value":610},{"type":49,"value":799}," MAUI has no built-in request scope like ASP.NET Core. MAUI creates one ",{"type":43,"tag":58,"props":801,"children":803},{"className":802},[],[804],{"type":49,"value":369},{"type":49,"value":806}," per window, so a Scoped service lives as long as that window; resolved from the root provider it silently behaves as a Singleton. Neither gives per-navigation freshness.",{"type":43,"tag":637,"props":808,"children":809},{},[],{"type":43,"tag":83,"props":811,"children":813},{"id":812},"registration-pattern-in-mauiprogramcs",[814],{"type":49,"value":815},"Registration Pattern in MauiProgram.cs",{"type":43,"tag":494,"props":817,"children":819},{"className":496,"code":818,"language":498,"meta":499,"style":499},"public static MauiApp CreateMauiApp()\n{\n    var builder = MauiApp.CreateBuilder();\n    builder.UseMauiApp\u003CApp>();\n\n    \u002F\u002F Services — Singleton for shared state\n    builder.Services.AddSingleton\u003CIDataService, DataService>();\n    builder.Services.AddSingleton\u003CISettingsService, SettingsService>();\n\n    \u002F\u002F HTTP — use typed or named clients via IHttpClientFactory\n    \u002F\u002F Requires NuGet: Microsoft.Extensions.Http\n    builder.Services.AddHttpClient\u003CIApiClient, ApiClient>();\n\n    \u002F\u002F ViewModels — Transient for fresh state per navigation\n    builder.Services.AddTransient\u003CMainViewModel>();\n    builder.Services.AddTransient\u003CDetailViewModel>();\n\n    \u002F\u002F Pages — Transient so constructor injection fires each time\n    builder.Services.AddTransient\u003CMainPage>();\n    builder.Services.AddTransient\u003CDetailPage>();\n\n    return builder.Build();\n}\n",[820],{"type":43,"tag":58,"props":821,"children":822},{"__ignoreMap":499},[823,831,839,847,856,866,875,884,893,901,910,919,928,936,945,954,963,971,980,989,998,1006,1015],{"type":43,"tag":505,"props":824,"children":825},{"class":507,"line":508},[826],{"type":43,"tag":505,"props":827,"children":828},{},[829],{"type":49,"value":830},"public static MauiApp CreateMauiApp()\n",{"type":43,"tag":505,"props":832,"children":833},{"class":507,"line":517},[834],{"type":43,"tag":505,"props":835,"children":836},{},[837],{"type":49,"value":838},"{\n",{"type":43,"tag":505,"props":840,"children":841},{"class":507,"line":526},[842],{"type":43,"tag":505,"props":843,"children":844},{},[845],{"type":49,"value":846},"    var builder = MauiApp.CreateBuilder();\n",{"type":43,"tag":505,"props":848,"children":850},{"class":507,"line":849},4,[851],{"type":43,"tag":505,"props":852,"children":853},{},[854],{"type":49,"value":855},"    builder.UseMauiApp\u003CApp>();\n",{"type":43,"tag":505,"props":857,"children":859},{"class":507,"line":858},5,[860],{"type":43,"tag":505,"props":861,"children":863},{"emptyLinePlaceholder":862},true,[864],{"type":49,"value":865},"\n",{"type":43,"tag":505,"props":867,"children":869},{"class":507,"line":868},6,[870],{"type":43,"tag":505,"props":871,"children":872},{},[873],{"type":49,"value":874},"    \u002F\u002F Services — Singleton for shared state\n",{"type":43,"tag":505,"props":876,"children":878},{"class":507,"line":877},7,[879],{"type":43,"tag":505,"props":880,"children":881},{},[882],{"type":49,"value":883},"    builder.Services.AddSingleton\u003CIDataService, DataService>();\n",{"type":43,"tag":505,"props":885,"children":887},{"class":507,"line":886},8,[888],{"type":43,"tag":505,"props":889,"children":890},{},[891],{"type":49,"value":892},"    builder.Services.AddSingleton\u003CISettingsService, SettingsService>();\n",{"type":43,"tag":505,"props":894,"children":896},{"class":507,"line":895},9,[897],{"type":43,"tag":505,"props":898,"children":899},{"emptyLinePlaceholder":862},[900],{"type":49,"value":865},{"type":43,"tag":505,"props":902,"children":904},{"class":507,"line":903},10,[905],{"type":43,"tag":505,"props":906,"children":907},{},[908],{"type":49,"value":909},"    \u002F\u002F HTTP — use typed or named clients via IHttpClientFactory\n",{"type":43,"tag":505,"props":911,"children":913},{"class":507,"line":912},11,[914],{"type":43,"tag":505,"props":915,"children":916},{},[917],{"type":49,"value":918},"    \u002F\u002F Requires NuGet: Microsoft.Extensions.Http\n",{"type":43,"tag":505,"props":920,"children":922},{"class":507,"line":921},12,[923],{"type":43,"tag":505,"props":924,"children":925},{},[926],{"type":49,"value":927},"    builder.Services.AddHttpClient\u003CIApiClient, ApiClient>();\n",{"type":43,"tag":505,"props":929,"children":931},{"class":507,"line":930},13,[932],{"type":43,"tag":505,"props":933,"children":934},{"emptyLinePlaceholder":862},[935],{"type":49,"value":865},{"type":43,"tag":505,"props":937,"children":939},{"class":507,"line":938},14,[940],{"type":43,"tag":505,"props":941,"children":942},{},[943],{"type":49,"value":944},"    \u002F\u002F ViewModels — Transient for fresh state per navigation\n",{"type":43,"tag":505,"props":946,"children":948},{"class":507,"line":947},15,[949],{"type":43,"tag":505,"props":950,"children":951},{},[952],{"type":49,"value":953},"    builder.Services.AddTransient\u003CMainViewModel>();\n",{"type":43,"tag":505,"props":955,"children":957},{"class":507,"line":956},16,[958],{"type":43,"tag":505,"props":959,"children":960},{},[961],{"type":49,"value":962},"    builder.Services.AddTransient\u003CDetailViewModel>();\n",{"type":43,"tag":505,"props":964,"children":966},{"class":507,"line":965},17,[967],{"type":43,"tag":505,"props":968,"children":969},{"emptyLinePlaceholder":862},[970],{"type":49,"value":865},{"type":43,"tag":505,"props":972,"children":974},{"class":507,"line":973},18,[975],{"type":43,"tag":505,"props":976,"children":977},{},[978],{"type":49,"value":979},"    \u002F\u002F Pages — Transient so constructor injection fires each time\n",{"type":43,"tag":505,"props":981,"children":983},{"class":507,"line":982},19,[984],{"type":43,"tag":505,"props":985,"children":986},{},[987],{"type":49,"value":988},"    builder.Services.AddTransient\u003CMainPage>();\n",{"type":43,"tag":505,"props":990,"children":992},{"class":507,"line":991},20,[993],{"type":43,"tag":505,"props":994,"children":995},{},[996],{"type":49,"value":997},"    builder.Services.AddTransient\u003CDetailPage>();\n",{"type":43,"tag":505,"props":999,"children":1001},{"class":507,"line":1000},21,[1002],{"type":43,"tag":505,"props":1003,"children":1004},{"emptyLinePlaceholder":862},[1005],{"type":49,"value":865},{"type":43,"tag":505,"props":1007,"children":1009},{"class":507,"line":1008},22,[1010],{"type":43,"tag":505,"props":1011,"children":1012},{},[1013],{"type":49,"value":1014},"    return builder.Build();\n",{"type":43,"tag":505,"props":1016,"children":1018},{"class":507,"line":1017},23,[1019],{"type":43,"tag":505,"props":1020,"children":1021},{},[1022],{"type":49,"value":1023},"}\n",{"type":43,"tag":637,"props":1025,"children":1026},{},[],{"type":43,"tag":83,"props":1028,"children":1030},{"id":1029},"constructor-injection",[1031],{"type":49,"value":1032},"Constructor Injection",{"type":43,"tag":52,"props":1034,"children":1035},{},[1036],{"type":49,"value":1037},"Inject dependencies through constructor parameters. The container resolves them automatically when the type is itself resolved from DI.",{"type":43,"tag":494,"props":1039,"children":1041},{"className":496,"code":1040,"language":498,"meta":499,"style":499},"public class MainViewModel\n{\n    private readonly IDataService _dataService;\n\n    public MainViewModel(IDataService dataService)\n    {\n        _dataService = dataService;\n    }\n\n    public async Task LoadAsync() => Items = await _dataService.GetItemsAsync();\n}\n",[1042],{"type":43,"tag":58,"props":1043,"children":1044},{"__ignoreMap":499},[1045,1053,1060,1068,1075,1083,1091,1099,1107,1114,1122],{"type":43,"tag":505,"props":1046,"children":1047},{"class":507,"line":508},[1048],{"type":43,"tag":505,"props":1049,"children":1050},{},[1051],{"type":49,"value":1052},"public class MainViewModel\n",{"type":43,"tag":505,"props":1054,"children":1055},{"class":507,"line":517},[1056],{"type":43,"tag":505,"props":1057,"children":1058},{},[1059],{"type":49,"value":838},{"type":43,"tag":505,"props":1061,"children":1062},{"class":507,"line":526},[1063],{"type":43,"tag":505,"props":1064,"children":1065},{},[1066],{"type":49,"value":1067},"    private readonly IDataService _dataService;\n",{"type":43,"tag":505,"props":1069,"children":1070},{"class":507,"line":849},[1071],{"type":43,"tag":505,"props":1072,"children":1073},{"emptyLinePlaceholder":862},[1074],{"type":49,"value":865},{"type":43,"tag":505,"props":1076,"children":1077},{"class":507,"line":858},[1078],{"type":43,"tag":505,"props":1079,"children":1080},{},[1081],{"type":49,"value":1082},"    public MainViewModel(IDataService dataService)\n",{"type":43,"tag":505,"props":1084,"children":1085},{"class":507,"line":868},[1086],{"type":43,"tag":505,"props":1087,"children":1088},{},[1089],{"type":49,"value":1090},"    {\n",{"type":43,"tag":505,"props":1092,"children":1093},{"class":507,"line":877},[1094],{"type":43,"tag":505,"props":1095,"children":1096},{},[1097],{"type":49,"value":1098},"        _dataService = dataService;\n",{"type":43,"tag":505,"props":1100,"children":1101},{"class":507,"line":886},[1102],{"type":43,"tag":505,"props":1103,"children":1104},{},[1105],{"type":49,"value":1106},"    }\n",{"type":43,"tag":505,"props":1108,"children":1109},{"class":507,"line":895},[1110],{"type":43,"tag":505,"props":1111,"children":1112},{"emptyLinePlaceholder":862},[1113],{"type":49,"value":865},{"type":43,"tag":505,"props":1115,"children":1116},{"class":507,"line":903},[1117],{"type":43,"tag":505,"props":1118,"children":1119},{},[1120],{"type":49,"value":1121},"    public async Task LoadAsync() => Items = await _dataService.GetItemsAsync();\n",{"type":43,"tag":505,"props":1123,"children":1124},{"class":507,"line":912},[1125],{"type":43,"tag":505,"props":1126,"children":1127},{},[1128],{"type":49,"value":1023},{"type":43,"tag":1130,"props":1131,"children":1133},"h3",{"id":1132},"viewmodel-page-wiring",[1134],{"type":49,"value":1135},"ViewModel → Page Wiring",{"type":43,"tag":52,"props":1137,"children":1138},{},[1139,1141,1146],{"type":49,"value":1140},"Register both Page and ViewModel. Inject the ViewModel into the Page and assign it as ",{"type":43,"tag":58,"props":1142,"children":1144},{"className":1143},[],[1145],{"type":49,"value":608},{"type":49,"value":1147},":",{"type":43,"tag":494,"props":1149,"children":1151},{"className":496,"code":1150,"language":498,"meta":499,"style":499},"public partial class MainPage : ContentPage\n{\n    public MainPage(MainViewModel viewModel)\n    {\n        InitializeComponent();\n        BindingContext = viewModel;\n    }\n}\n",[1152],{"type":43,"tag":58,"props":1153,"children":1154},{"__ignoreMap":499},[1155,1163,1170,1178,1185,1193,1201,1208],{"type":43,"tag":505,"props":1156,"children":1157},{"class":507,"line":508},[1158],{"type":43,"tag":505,"props":1159,"children":1160},{},[1161],{"type":49,"value":1162},"public partial class MainPage : ContentPage\n",{"type":43,"tag":505,"props":1164,"children":1165},{"class":507,"line":517},[1166],{"type":43,"tag":505,"props":1167,"children":1168},{},[1169],{"type":49,"value":838},{"type":43,"tag":505,"props":1171,"children":1172},{"class":507,"line":526},[1173],{"type":43,"tag":505,"props":1174,"children":1175},{},[1176],{"type":49,"value":1177},"    public MainPage(MainViewModel viewModel)\n",{"type":43,"tag":505,"props":1179,"children":1180},{"class":507,"line":849},[1181],{"type":43,"tag":505,"props":1182,"children":1183},{},[1184],{"type":49,"value":1090},{"type":43,"tag":505,"props":1186,"children":1187},{"class":507,"line":858},[1188],{"type":43,"tag":505,"props":1189,"children":1190},{},[1191],{"type":49,"value":1192},"        InitializeComponent();\n",{"type":43,"tag":505,"props":1194,"children":1195},{"class":507,"line":868},[1196],{"type":43,"tag":505,"props":1197,"children":1198},{},[1199],{"type":49,"value":1200},"        BindingContext = viewModel;\n",{"type":43,"tag":505,"props":1202,"children":1203},{"class":507,"line":877},[1204],{"type":43,"tag":505,"props":1205,"children":1206},{},[1207],{"type":49,"value":1106},{"type":43,"tag":505,"props":1209,"children":1210},{"class":507,"line":886},[1211],{"type":43,"tag":505,"props":1212,"children":1213},{},[1214],{"type":49,"value":1023},{"type":43,"tag":637,"props":1216,"children":1217},{},[],{"type":43,"tag":83,"props":1219,"children":1221},{"id":1220},"shell-navigation-auto-resolution",[1222],{"type":49,"value":1223},"Shell Navigation Auto-Resolution",{"type":43,"tag":52,"props":1225,"children":1226},{},[1227,1229,1233],{"type":49,"value":1228},"When a Page is registered in DI ",{"type":43,"tag":175,"props":1230,"children":1231},{},[1232],{"type":49,"value":389},{"type":49,"value":1234}," as a Shell route, Shell resolves it (and its full dependency graph) automatically on navigation:",{"type":43,"tag":494,"props":1236,"children":1238},{"className":496,"code":1237,"language":498,"meta":499,"style":499},"\u002F\u002F MauiProgram.cs\nbuilder.Services.AddTransient\u003CDetailPage>();\nbuilder.Services.AddTransient\u003CDetailViewModel>();\n\n\u002F\u002F AppShell.xaml.cs\nRouting.RegisterRoute(nameof(DetailPage), typeof(DetailPage));\n\n\u002F\u002F Navigate — DI resolves DetailPage + DetailViewModel\nawait Shell.Current.GoToAsync(nameof(DetailPage));\n",[1239],{"type":43,"tag":58,"props":1240,"children":1241},{"__ignoreMap":499},[1242,1250,1258,1266,1273,1281,1289,1296,1304],{"type":43,"tag":505,"props":1243,"children":1244},{"class":507,"line":508},[1245],{"type":43,"tag":505,"props":1246,"children":1247},{},[1248],{"type":49,"value":1249},"\u002F\u002F MauiProgram.cs\n",{"type":43,"tag":505,"props":1251,"children":1252},{"class":507,"line":517},[1253],{"type":43,"tag":505,"props":1254,"children":1255},{},[1256],{"type":49,"value":1257},"builder.Services.AddTransient\u003CDetailPage>();\n",{"type":43,"tag":505,"props":1259,"children":1260},{"class":507,"line":526},[1261],{"type":43,"tag":505,"props":1262,"children":1263},{},[1264],{"type":49,"value":1265},"builder.Services.AddTransient\u003CDetailViewModel>();\n",{"type":43,"tag":505,"props":1267,"children":1268},{"class":507,"line":849},[1269],{"type":43,"tag":505,"props":1270,"children":1271},{"emptyLinePlaceholder":862},[1272],{"type":49,"value":865},{"type":43,"tag":505,"props":1274,"children":1275},{"class":507,"line":858},[1276],{"type":43,"tag":505,"props":1277,"children":1278},{},[1279],{"type":49,"value":1280},"\u002F\u002F AppShell.xaml.cs\n",{"type":43,"tag":505,"props":1282,"children":1283},{"class":507,"line":868},[1284],{"type":43,"tag":505,"props":1285,"children":1286},{},[1287],{"type":49,"value":1288},"Routing.RegisterRoute(nameof(DetailPage), typeof(DetailPage));\n",{"type":43,"tag":505,"props":1290,"children":1291},{"class":507,"line":877},[1292],{"type":43,"tag":505,"props":1293,"children":1294},{"emptyLinePlaceholder":862},[1295],{"type":49,"value":865},{"type":43,"tag":505,"props":1297,"children":1298},{"class":507,"line":886},[1299],{"type":43,"tag":505,"props":1300,"children":1301},{},[1302],{"type":49,"value":1303},"\u002F\u002F Navigate — DI resolves DetailPage + DetailViewModel\n",{"type":43,"tag":505,"props":1305,"children":1306},{"class":507,"line":895},[1307],{"type":43,"tag":505,"props":1308,"children":1309},{},[1310],{"type":49,"value":1311},"await Shell.Current.GoToAsync(nameof(DetailPage));\n",{"type":43,"tag":1130,"props":1313,"children":1315},{"id":1314},"passing-parameters-to-a-di-resolved-viewmodel",[1316],{"type":49,"value":1317},"Passing parameters to a DI-resolved ViewModel",{"type":43,"tag":52,"props":1319,"children":1320},{},[1321,1323,1329,1331,1337],{"type":49,"value":1322},"DI supplies the ViewModel's ",{"type":43,"tag":1324,"props":1325,"children":1326},"em",{},[1327],{"type":49,"value":1328},"dependencies",{"type":49,"value":1330},"; navigation parameters arrive separately.\nDon't try to inject them through the constructor — implement ",{"type":43,"tag":58,"props":1332,"children":1334},{"className":1333},[],[1335],{"type":49,"value":1336},"IQueryAttributable",{"type":49,"value":1338},"\non the ViewModel so it receives both:",{"type":43,"tag":494,"props":1340,"children":1342},{"className":496,"code":1341,"language":498,"meta":499,"style":499},"public class DetailViewModel : ObservableObject, IQueryAttributable\n{\n    readonly IDataService _data;   \u002F\u002F ← injected by DI\n\n    public DetailViewModel(IDataService data) => _data = data;\n\n    public void ApplyQueryAttributes(IDictionary\u003Cstring, object> query)\n    {\n        \u002F\u002F ← supplied by navigation\n        if (query.TryGetValue(\"id\", out var id))\n            LoadAsync(id.ToString()!);\n    }\n}\n\n\u002F\u002F Navigate with a parameter — the page and its ViewModel still come from DI\nawait Shell.Current.GoToAsync($\"{nameof(DetailPage)}?id={product.Id}\");\n",[1343],{"type":43,"tag":58,"props":1344,"children":1345},{"__ignoreMap":499},[1346,1354,1361,1369,1376,1384,1391,1399,1406,1414,1422,1430,1437,1444,1451,1459],{"type":43,"tag":505,"props":1347,"children":1348},{"class":507,"line":508},[1349],{"type":43,"tag":505,"props":1350,"children":1351},{},[1352],{"type":49,"value":1353},"public class DetailViewModel : ObservableObject, IQueryAttributable\n",{"type":43,"tag":505,"props":1355,"children":1356},{"class":507,"line":517},[1357],{"type":43,"tag":505,"props":1358,"children":1359},{},[1360],{"type":49,"value":838},{"type":43,"tag":505,"props":1362,"children":1363},{"class":507,"line":526},[1364],{"type":43,"tag":505,"props":1365,"children":1366},{},[1367],{"type":49,"value":1368},"    readonly IDataService _data;   \u002F\u002F ← injected by DI\n",{"type":43,"tag":505,"props":1370,"children":1371},{"class":507,"line":849},[1372],{"type":43,"tag":505,"props":1373,"children":1374},{"emptyLinePlaceholder":862},[1375],{"type":49,"value":865},{"type":43,"tag":505,"props":1377,"children":1378},{"class":507,"line":858},[1379],{"type":43,"tag":505,"props":1380,"children":1381},{},[1382],{"type":49,"value":1383},"    public DetailViewModel(IDataService data) => _data = data;\n",{"type":43,"tag":505,"props":1385,"children":1386},{"class":507,"line":868},[1387],{"type":43,"tag":505,"props":1388,"children":1389},{"emptyLinePlaceholder":862},[1390],{"type":49,"value":865},{"type":43,"tag":505,"props":1392,"children":1393},{"class":507,"line":877},[1394],{"type":43,"tag":505,"props":1395,"children":1396},{},[1397],{"type":49,"value":1398},"    public void ApplyQueryAttributes(IDictionary\u003Cstring, object> query)\n",{"type":43,"tag":505,"props":1400,"children":1401},{"class":507,"line":886},[1402],{"type":43,"tag":505,"props":1403,"children":1404},{},[1405],{"type":49,"value":1090},{"type":43,"tag":505,"props":1407,"children":1408},{"class":507,"line":895},[1409],{"type":43,"tag":505,"props":1410,"children":1411},{},[1412],{"type":49,"value":1413},"        \u002F\u002F ← supplied by navigation\n",{"type":43,"tag":505,"props":1415,"children":1416},{"class":507,"line":903},[1417],{"type":43,"tag":505,"props":1418,"children":1419},{},[1420],{"type":49,"value":1421},"        if (query.TryGetValue(\"id\", out var id))\n",{"type":43,"tag":505,"props":1423,"children":1424},{"class":507,"line":912},[1425],{"type":43,"tag":505,"props":1426,"children":1427},{},[1428],{"type":49,"value":1429},"            LoadAsync(id.ToString()!);\n",{"type":43,"tag":505,"props":1431,"children":1432},{"class":507,"line":921},[1433],{"type":43,"tag":505,"props":1434,"children":1435},{},[1436],{"type":49,"value":1106},{"type":43,"tag":505,"props":1438,"children":1439},{"class":507,"line":930},[1440],{"type":43,"tag":505,"props":1441,"children":1442},{},[1443],{"type":49,"value":1023},{"type":43,"tag":505,"props":1445,"children":1446},{"class":507,"line":938},[1447],{"type":43,"tag":505,"props":1448,"children":1449},{"emptyLinePlaceholder":862},[1450],{"type":49,"value":865},{"type":43,"tag":505,"props":1452,"children":1453},{"class":507,"line":947},[1454],{"type":43,"tag":505,"props":1455,"children":1456},{},[1457],{"type":49,"value":1458},"\u002F\u002F Navigate with a parameter — the page and its ViewModel still come from DI\n",{"type":43,"tag":505,"props":1460,"children":1461},{"class":507,"line":956},[1462],{"type":43,"tag":505,"props":1463,"children":1464},{},[1465],{"type":49,"value":1466},"await Shell.Current.GoToAsync($\"{nameof(DetailPage)}?id={product.Id}\");\n",{"type":43,"tag":52,"props":1468,"children":1469},{},[1470,1472,1476,1478,1483],{"type":49,"value":1471},"Shell applies query attributes to the page ",{"type":43,"tag":175,"props":1473,"children":1474},{},[1475],{"type":49,"value":389},{"type":49,"value":1477}," its ",{"type":43,"tag":58,"props":1479,"children":1481},{"className":1480},[],[1482],{"type":49,"value":608},{"type":49,"value":1484},", so the\nViewModel receives them without any wiring in the page.",{"type":43,"tag":637,"props":1486,"children":1487},{},[],{"type":43,"tag":83,"props":1489,"children":1491},{"id":1490},"platform-specific-registration",[1492],{"type":49,"value":1493},"Platform-Specific Registration",{"type":43,"tag":52,"props":1495,"children":1496},{},[1497,1499,1504],{"type":49,"value":1498},"Use preprocessor directives to register platform implementations. Always cover every target platform or provide a no-op fallback to avoid runtime ",{"type":43,"tag":58,"props":1500,"children":1502},{"className":1501},[],[1503],{"type":49,"value":633},{"type":49,"value":610},{"type":43,"tag":494,"props":1506,"children":1508},{"className":496,"code":1507,"language":498,"meta":499,"style":499},"#if ANDROID\nbuilder.Services.AddSingleton\u003CINotificationService, AndroidNotificationService>();\n#elif IOS || MACCATALYST\nbuilder.Services.AddSingleton\u003CINotificationService, AppleNotificationService>();\n#elif WINDOWS\nbuilder.Services.AddSingleton\u003CINotificationService, WindowsNotificationService>();\n#else\nbuilder.Services.AddSingleton\u003CINotificationService, NoOpNotificationService>();\n#endif\n",[1509],{"type":43,"tag":58,"props":1510,"children":1511},{"__ignoreMap":499},[1512,1520,1528,1536,1544,1552,1560,1568,1576],{"type":43,"tag":505,"props":1513,"children":1514},{"class":507,"line":508},[1515],{"type":43,"tag":505,"props":1516,"children":1517},{},[1518],{"type":49,"value":1519},"#if ANDROID\n",{"type":43,"tag":505,"props":1521,"children":1522},{"class":507,"line":517},[1523],{"type":43,"tag":505,"props":1524,"children":1525},{},[1526],{"type":49,"value":1527},"builder.Services.AddSingleton\u003CINotificationService, AndroidNotificationService>();\n",{"type":43,"tag":505,"props":1529,"children":1530},{"class":507,"line":526},[1531],{"type":43,"tag":505,"props":1532,"children":1533},{},[1534],{"type":49,"value":1535},"#elif IOS || MACCATALYST\n",{"type":43,"tag":505,"props":1537,"children":1538},{"class":507,"line":849},[1539],{"type":43,"tag":505,"props":1540,"children":1541},{},[1542],{"type":49,"value":1543},"builder.Services.AddSingleton\u003CINotificationService, AppleNotificationService>();\n",{"type":43,"tag":505,"props":1545,"children":1546},{"class":507,"line":858},[1547],{"type":43,"tag":505,"props":1548,"children":1549},{},[1550],{"type":49,"value":1551},"#elif WINDOWS\n",{"type":43,"tag":505,"props":1553,"children":1554},{"class":507,"line":868},[1555],{"type":43,"tag":505,"props":1556,"children":1557},{},[1558],{"type":49,"value":1559},"builder.Services.AddSingleton\u003CINotificationService, WindowsNotificationService>();\n",{"type":43,"tag":505,"props":1561,"children":1562},{"class":507,"line":877},[1563],{"type":43,"tag":505,"props":1564,"children":1565},{},[1566],{"type":49,"value":1567},"#else\n",{"type":43,"tag":505,"props":1569,"children":1570},{"class":507,"line":886},[1571],{"type":43,"tag":505,"props":1572,"children":1573},{},[1574],{"type":49,"value":1575},"builder.Services.AddSingleton\u003CINotificationService, NoOpNotificationService>();\n",{"type":43,"tag":505,"props":1577,"children":1578},{"class":507,"line":895},[1579],{"type":43,"tag":505,"props":1580,"children":1581},{},[1582],{"type":49,"value":1583},"#endif\n",{"type":43,"tag":637,"props":1585,"children":1586},{},[],{"type":43,"tag":83,"props":1588,"children":1590},{"id":1589},"explicit-resolution-last-resort",[1591],{"type":49,"value":1592},"Explicit Resolution (Last Resort)",{"type":43,"tag":52,"props":1594,"children":1595},{},[1596],{"type":49,"value":1597},"Prefer constructor injection. Use explicit resolution only where injection is genuinely unavailable (custom handlers, platform callbacks):",{"type":43,"tag":494,"props":1599,"children":1601},{"className":496,"code":1600,"language":498,"meta":499,"style":499},"\u002F\u002F From any Element with a Handler\nvar service = this.Handler.MauiContext.Services.GetService\u003CIDataService>();\n",[1602],{"type":43,"tag":58,"props":1603,"children":1604},{"__ignoreMap":499},[1605,1613],{"type":43,"tag":505,"props":1606,"children":1607},{"class":507,"line":508},[1608],{"type":43,"tag":505,"props":1609,"children":1610},{},[1611],{"type":49,"value":1612},"\u002F\u002F From any Element with a Handler\n",{"type":43,"tag":505,"props":1614,"children":1615},{"class":507,"line":517},[1616],{"type":43,"tag":505,"props":1617,"children":1618},{},[1619],{"type":49,"value":1620},"var service = this.Handler.MauiContext.Services.GetService\u003CIDataService>();\n",{"type":43,"tag":52,"props":1622,"children":1623},{},[1624,1626,1632],{"type":49,"value":1625},"For dynamic resolution, inject ",{"type":43,"tag":58,"props":1627,"children":1629},{"className":1628},[],[1630],{"type":49,"value":1631},"IServiceProvider",{"type":49,"value":1147},{"type":43,"tag":494,"props":1634,"children":1636},{"className":496,"code":1635,"language":498,"meta":499,"style":499},"public class NavigationService(IServiceProvider serviceProvider)\n{\n    public T ResolvePage\u003CT>() where T : Page\n        => serviceProvider.GetRequiredService\u003CT>();\n}\n",[1637],{"type":43,"tag":58,"props":1638,"children":1639},{"__ignoreMap":499},[1640,1648,1655,1663,1671],{"type":43,"tag":505,"props":1641,"children":1642},{"class":507,"line":508},[1643],{"type":43,"tag":505,"props":1644,"children":1645},{},[1646],{"type":49,"value":1647},"public class NavigationService(IServiceProvider serviceProvider)\n",{"type":43,"tag":505,"props":1649,"children":1650},{"class":507,"line":517},[1651],{"type":43,"tag":505,"props":1652,"children":1653},{},[1654],{"type":49,"value":838},{"type":43,"tag":505,"props":1656,"children":1657},{"class":507,"line":526},[1658],{"type":43,"tag":505,"props":1659,"children":1660},{},[1661],{"type":49,"value":1662},"    public T ResolvePage\u003CT>() where T : Page\n",{"type":43,"tag":505,"props":1664,"children":1665},{"class":507,"line":849},[1666],{"type":43,"tag":505,"props":1667,"children":1668},{},[1669],{"type":49,"value":1670},"        => serviceProvider.GetRequiredService\u003CT>();\n",{"type":43,"tag":505,"props":1672,"children":1673},{"class":507,"line":858},[1674],{"type":43,"tag":505,"props":1675,"children":1676},{},[1677],{"type":49,"value":1023},{"type":43,"tag":637,"props":1679,"children":1680},{},[],{"type":43,"tag":83,"props":1682,"children":1684},{"id":1683},"interface-first-pattern-for-testability",[1685],{"type":49,"value":1686},"Interface-First Pattern for Testability",{"type":43,"tag":52,"props":1688,"children":1689},{},[1690],{"type":49,"value":1691},"Define interfaces for every service so implementations can be swapped in tests:",{"type":43,"tag":494,"props":1693,"children":1695},{"className":496,"code":1694,"language":498,"meta":499,"style":499},"public interface IDataService\n{\n    Task\u003CList\u003CItem>> GetItemsAsync();\n}\n\n\u002F\u002F Production registration\nbuilder.Services.AddSingleton\u003CIDataService, DataService>();\n\n\u002F\u002F Test registration — swap without touching production code\nvar services = new ServiceCollection();\nservices.AddSingleton\u003CIDataService, FakeDataService>();\n",[1696],{"type":43,"tag":58,"props":1697,"children":1698},{"__ignoreMap":499},[1699,1707,1714,1722,1729,1736,1744,1752,1759,1767,1775],{"type":43,"tag":505,"props":1700,"children":1701},{"class":507,"line":508},[1702],{"type":43,"tag":505,"props":1703,"children":1704},{},[1705],{"type":49,"value":1706},"public interface IDataService\n",{"type":43,"tag":505,"props":1708,"children":1709},{"class":507,"line":517},[1710],{"type":43,"tag":505,"props":1711,"children":1712},{},[1713],{"type":49,"value":838},{"type":43,"tag":505,"props":1715,"children":1716},{"class":507,"line":526},[1717],{"type":43,"tag":505,"props":1718,"children":1719},{},[1720],{"type":49,"value":1721},"    Task\u003CList\u003CItem>> GetItemsAsync();\n",{"type":43,"tag":505,"props":1723,"children":1724},{"class":507,"line":849},[1725],{"type":43,"tag":505,"props":1726,"children":1727},{},[1728],{"type":49,"value":1023},{"type":43,"tag":505,"props":1730,"children":1731},{"class":507,"line":858},[1732],{"type":43,"tag":505,"props":1733,"children":1734},{"emptyLinePlaceholder":862},[1735],{"type":49,"value":865},{"type":43,"tag":505,"props":1737,"children":1738},{"class":507,"line":868},[1739],{"type":43,"tag":505,"props":1740,"children":1741},{},[1742],{"type":49,"value":1743},"\u002F\u002F Production registration\n",{"type":43,"tag":505,"props":1745,"children":1746},{"class":507,"line":877},[1747],{"type":43,"tag":505,"props":1748,"children":1749},{},[1750],{"type":49,"value":1751},"builder.Services.AddSingleton\u003CIDataService, DataService>();\n",{"type":43,"tag":505,"props":1753,"children":1754},{"class":507,"line":886},[1755],{"type":43,"tag":505,"props":1756,"children":1757},{"emptyLinePlaceholder":862},[1758],{"type":49,"value":865},{"type":43,"tag":505,"props":1760,"children":1761},{"class":507,"line":895},[1762],{"type":43,"tag":505,"props":1763,"children":1764},{},[1765],{"type":49,"value":1766},"\u002F\u002F Test registration — swap without touching production code\n",{"type":43,"tag":505,"props":1768,"children":1769},{"class":507,"line":903},[1770],{"type":43,"tag":505,"props":1771,"children":1772},{},[1773],{"type":49,"value":1774},"var services = new ServiceCollection();\n",{"type":43,"tag":505,"props":1776,"children":1777},{"class":507,"line":912},[1778],{"type":43,"tag":505,"props":1779,"children":1780},{},[1781],{"type":49,"value":1782},"services.AddSingleton\u003CIDataService, FakeDataService>();\n",{"type":43,"tag":637,"props":1784,"children":1785},{},[],{"type":43,"tag":83,"props":1787,"children":1789},{"id":1788},"common-pitfalls",[1790],{"type":49,"value":1791},"Common Pitfalls",{"type":43,"tag":1130,"props":1793,"children":1795},{"id":1794},"_1-singleton-viewmodels-cause-stale-data",[1796],{"type":49,"value":1797},"1. Singleton ViewModels Cause Stale Data",{"type":43,"tag":494,"props":1799,"children":1801},{"className":496,"code":1800,"language":498,"meta":499,"style":499},"\u002F\u002F ❌ ViewModel keeps stale state across navigations\nbuilder.Services.AddSingleton\u003CDetailViewModel>();\n\n\u002F\u002F ✅ Fresh instance each navigation\nbuilder.Services.AddTransient\u003CDetailViewModel>();\n",[1802],{"type":43,"tag":58,"props":1803,"children":1804},{"__ignoreMap":499},[1805,1813,1821,1828,1836],{"type":43,"tag":505,"props":1806,"children":1807},{"class":507,"line":508},[1808],{"type":43,"tag":505,"props":1809,"children":1810},{},[1811],{"type":49,"value":1812},"\u002F\u002F ❌ ViewModel keeps stale state across navigations\n",{"type":43,"tag":505,"props":1814,"children":1815},{"class":507,"line":517},[1816],{"type":43,"tag":505,"props":1817,"children":1818},{},[1819],{"type":49,"value":1820},"builder.Services.AddSingleton\u003CDetailViewModel>();\n",{"type":43,"tag":505,"props":1822,"children":1823},{"class":507,"line":526},[1824],{"type":43,"tag":505,"props":1825,"children":1826},{"emptyLinePlaceholder":862},[1827],{"type":49,"value":865},{"type":43,"tag":505,"props":1829,"children":1830},{"class":507,"line":849},[1831],{"type":43,"tag":505,"props":1832,"children":1833},{},[1834],{"type":49,"value":1835},"\u002F\u002F ✅ Fresh instance each navigation\n",{"type":43,"tag":505,"props":1837,"children":1838},{"class":507,"line":858},[1839],{"type":43,"tag":505,"props":1840,"children":1841},{},[1842],{"type":49,"value":1265},{"type":43,"tag":1130,"props":1844,"children":1846},{"id":1845},"_2-contenttemplate-pages-are-not-created-through-di",[1847],{"type":49,"value":1848},"2. ContentTemplate Pages Are Not Created Through DI",{"type":43,"tag":52,"props":1850,"children":1851},{},[1852,1854,1860,1862,1868,1870,1876,1878,1883,1885,1891,1893,1898],{"type":49,"value":1853},"Pages declared in Shell XAML via ",{"type":43,"tag":58,"props":1855,"children":1857},{"className":1856},[],[1858],{"type":49,"value":1859},"\u003CShellContent ContentTemplate=\"{DataTemplate views:DetailPage}\">",{"type":49,"value":1861}," are instantiated with ",{"type":43,"tag":58,"props":1863,"children":1865},{"className":1864},[],[1866],{"type":49,"value":1867},"Activator.CreateInstance",{"type":49,"value":1869}," (",{"type":43,"tag":58,"props":1871,"children":1873},{"className":1872},[],[1874],{"type":49,"value":1875},"ElementTemplate.cs",{"type":49,"value":1877},"), ",{"type":43,"tag":175,"props":1879,"children":1880},{},[1881],{"type":49,"value":1882},"not",{"type":49,"value":1884}," through the service provider. Constructor injection does not run on that path: if the page's only constructor takes dependencies, you get a ",{"type":43,"tag":58,"props":1886,"children":1888},{"className":1887},[],[1889],{"type":49,"value":1890},"MissingMethodException",{"type":49,"value":1892}," — not a silently ",{"type":43,"tag":58,"props":1894,"children":1896},{"className":1895},[],[1897],{"type":49,"value":633},{"type":49,"value":1899}," dependency.",{"type":43,"tag":52,"props":1901,"children":1902},{},[1903,1905,1910,1912,1918,1920,1926,1927,1933,1935,1940],{"type":49,"value":1904},"Pages reached through ",{"type":43,"tag":58,"props":1906,"children":1908},{"className":1907},[],[1909],{"type":49,"value":397},{"type":49,"value":1911}," + ",{"type":43,"tag":58,"props":1913,"children":1915},{"className":1914},[],[1916],{"type":49,"value":1917},"GoToAsync",{"type":49,"value":1919}," are different: they go through ",{"type":43,"tag":58,"props":1921,"children":1923},{"className":1922},[],[1924],{"type":49,"value":1925},"ActivatorUtilities.GetServiceOrCreateInstance",{"type":49,"value":1869},{"type":43,"tag":58,"props":1928,"children":1930},{"className":1929},[],[1931],{"type":49,"value":1932},"Routing.cs",{"type":49,"value":1934},"), which injects registered dependencies even if the page type itself was never registered, and ",{"type":43,"tag":175,"props":1936,"children":1937},{},[1938],{"type":49,"value":1939},"throws",{"type":49,"value":1941}," if a required dependency cannot be resolved.",{"type":43,"tag":494,"props":1943,"children":1945},{"className":496,"code":1944,"language":498,"meta":499,"style":499},"\u002F\u002F Registering the page and its dependencies keeps both paths working\nbuilder.Services.AddTransient\u003CDetailPage>();\nbuilder.Services.AddTransient\u003CDetailViewModel>();\n",[1946],{"type":43,"tag":58,"props":1947,"children":1948},{"__ignoreMap":499},[1949,1957,1964],{"type":43,"tag":505,"props":1950,"children":1951},{"class":507,"line":508},[1952],{"type":43,"tag":505,"props":1953,"children":1954},{},[1955],{"type":49,"value":1956},"\u002F\u002F Registering the page and its dependencies keeps both paths working\n",{"type":43,"tag":505,"props":1958,"children":1959},{"class":507,"line":517},[1960],{"type":43,"tag":505,"props":1961,"children":1962},{},[1963],{"type":49,"value":1257},{"type":43,"tag":505,"props":1965,"children":1966},{"class":507,"line":526},[1967],{"type":43,"tag":505,"props":1968,"children":1969},{},[1970],{"type":49,"value":1265},{"type":43,"tag":52,"props":1972,"children":1973},{},[1974,1976,1982],{"type":49,"value":1975},"If you need DI for a tab\u002Fflyout page, give it a parameterless constructor that resolves what it needs, or navigate to it by route instead of embedding it in ",{"type":43,"tag":58,"props":1977,"children":1979},{"className":1978},[],[1980],{"type":49,"value":1981},"ContentTemplate",{"type":49,"value":610},{"type":43,"tag":1130,"props":1984,"children":1986},{"id":1985},"_3-xaml-resource-parsing-vs-di-timing",[1987],{"type":49,"value":1988},"3. XAML Resource Parsing vs. DI Timing",{"type":43,"tag":52,"props":1990,"children":1991},{},[1992,1994,2000,2002,2008,2010,2016],{"type":49,"value":1993},"XAML resources in ",{"type":43,"tag":58,"props":1995,"children":1997},{"className":1996},[],[1998],{"type":49,"value":1999},"App.xaml",{"type":49,"value":2001}," are parsed during ",{"type":43,"tag":58,"props":2003,"children":2005},{"className":2004},[],[2006],{"type":49,"value":2007},"InitializeComponent()",{"type":49,"value":2009}," — before the container is fully available. Defer service-dependent work to ",{"type":43,"tag":58,"props":2011,"children":2013},{"className":2012},[],[2014],{"type":49,"value":2015},"CreateWindow()",{"type":49,"value":1147},{"type":43,"tag":494,"props":2018,"children":2020},{"className":496,"code":2019,"language":498,"meta":499,"style":499},"public partial class App : Application\n{\n    private readonly IServiceProvider _services;\n\n    public App(IServiceProvider services)\n    {\n        _services = services;\n        InitializeComponent();\n    }\n\n    protected override Window CreateWindow(IActivationState? activationState)\n    {\n        \u002F\u002F Safe — container is fully built\n        \u002F\u002F Requires: builder.Services.AddTransient\u003CAppShell>() in MauiProgram.cs\n        var appShell = _services.GetRequiredService\u003CAppShell>();\n        return new Window(appShell);\n    }\n}\n",[2021],{"type":43,"tag":58,"props":2022,"children":2023},{"__ignoreMap":499},[2024,2032,2039,2047,2054,2062,2069,2077,2084,2091,2098,2106,2113,2121,2129,2137,2145,2152],{"type":43,"tag":505,"props":2025,"children":2026},{"class":507,"line":508},[2027],{"type":43,"tag":505,"props":2028,"children":2029},{},[2030],{"type":49,"value":2031},"public partial class App : Application\n",{"type":43,"tag":505,"props":2033,"children":2034},{"class":507,"line":517},[2035],{"type":43,"tag":505,"props":2036,"children":2037},{},[2038],{"type":49,"value":838},{"type":43,"tag":505,"props":2040,"children":2041},{"class":507,"line":526},[2042],{"type":43,"tag":505,"props":2043,"children":2044},{},[2045],{"type":49,"value":2046},"    private readonly IServiceProvider _services;\n",{"type":43,"tag":505,"props":2048,"children":2049},{"class":507,"line":849},[2050],{"type":43,"tag":505,"props":2051,"children":2052},{"emptyLinePlaceholder":862},[2053],{"type":49,"value":865},{"type":43,"tag":505,"props":2055,"children":2056},{"class":507,"line":858},[2057],{"type":43,"tag":505,"props":2058,"children":2059},{},[2060],{"type":49,"value":2061},"    public App(IServiceProvider services)\n",{"type":43,"tag":505,"props":2063,"children":2064},{"class":507,"line":868},[2065],{"type":43,"tag":505,"props":2066,"children":2067},{},[2068],{"type":49,"value":1090},{"type":43,"tag":505,"props":2070,"children":2071},{"class":507,"line":877},[2072],{"type":43,"tag":505,"props":2073,"children":2074},{},[2075],{"type":49,"value":2076},"        _services = services;\n",{"type":43,"tag":505,"props":2078,"children":2079},{"class":507,"line":886},[2080],{"type":43,"tag":505,"props":2081,"children":2082},{},[2083],{"type":49,"value":1192},{"type":43,"tag":505,"props":2085,"children":2086},{"class":507,"line":895},[2087],{"type":43,"tag":505,"props":2088,"children":2089},{},[2090],{"type":49,"value":1106},{"type":43,"tag":505,"props":2092,"children":2093},{"class":507,"line":903},[2094],{"type":43,"tag":505,"props":2095,"children":2096},{"emptyLinePlaceholder":862},[2097],{"type":49,"value":865},{"type":43,"tag":505,"props":2099,"children":2100},{"class":507,"line":912},[2101],{"type":43,"tag":505,"props":2102,"children":2103},{},[2104],{"type":49,"value":2105},"    protected override Window CreateWindow(IActivationState? activationState)\n",{"type":43,"tag":505,"props":2107,"children":2108},{"class":507,"line":921},[2109],{"type":43,"tag":505,"props":2110,"children":2111},{},[2112],{"type":49,"value":1090},{"type":43,"tag":505,"props":2114,"children":2115},{"class":507,"line":930},[2116],{"type":43,"tag":505,"props":2117,"children":2118},{},[2119],{"type":49,"value":2120},"        \u002F\u002F Safe — container is fully built\n",{"type":43,"tag":505,"props":2122,"children":2123},{"class":507,"line":938},[2124],{"type":43,"tag":505,"props":2125,"children":2126},{},[2127],{"type":49,"value":2128},"        \u002F\u002F Requires: builder.Services.AddTransient\u003CAppShell>() in MauiProgram.cs\n",{"type":43,"tag":505,"props":2130,"children":2131},{"class":507,"line":947},[2132],{"type":43,"tag":505,"props":2133,"children":2134},{},[2135],{"type":49,"value":2136},"        var appShell = _services.GetRequiredService\u003CAppShell>();\n",{"type":43,"tag":505,"props":2138,"children":2139},{"class":507,"line":956},[2140],{"type":43,"tag":505,"props":2141,"children":2142},{},[2143],{"type":49,"value":2144},"        return new Window(appShell);\n",{"type":43,"tag":505,"props":2146,"children":2147},{"class":507,"line":965},[2148],{"type":43,"tag":505,"props":2149,"children":2150},{},[2151],{"type":49,"value":1106},{"type":43,"tag":505,"props":2153,"children":2154},{"class":507,"line":973},[2155],{"type":43,"tag":505,"props":2156,"children":2157},{},[2158],{"type":49,"value":1023},{"type":43,"tag":1130,"props":2160,"children":2162},{"id":2161},"_4-service-locator-anti-pattern",[2163],{"type":49,"value":2164},"4. Service Locator Anti-Pattern",{"type":43,"tag":494,"props":2166,"children":2168},{"className":496,"code":2167,"language":498,"meta":499,"style":499},"\u002F\u002F ❌ Hides dependencies, hard to test\nvar svc = this.Handler.MauiContext.Services.GetService\u003CIDataService>();\n\n\u002F\u002F ✅ Constructor injection — explicit and testable\npublic class MyViewModel(IDataService dataService) { }\n",[2169],{"type":43,"tag":58,"props":2170,"children":2171},{"__ignoreMap":499},[2172,2180,2188,2195,2203],{"type":43,"tag":505,"props":2173,"children":2174},{"class":507,"line":508},[2175],{"type":43,"tag":505,"props":2176,"children":2177},{},[2178],{"type":49,"value":2179},"\u002F\u002F ❌ Hides dependencies, hard to test\n",{"type":43,"tag":505,"props":2181,"children":2182},{"class":507,"line":517},[2183],{"type":43,"tag":505,"props":2184,"children":2185},{},[2186],{"type":49,"value":2187},"var svc = this.Handler.MauiContext.Services.GetService\u003CIDataService>();\n",{"type":43,"tag":505,"props":2189,"children":2190},{"class":507,"line":526},[2191],{"type":43,"tag":505,"props":2192,"children":2193},{"emptyLinePlaceholder":862},[2194],{"type":49,"value":865},{"type":43,"tag":505,"props":2196,"children":2197},{"class":507,"line":849},[2198],{"type":43,"tag":505,"props":2199,"children":2200},{},[2201],{"type":49,"value":2202},"\u002F\u002F ✅ Constructor injection — explicit and testable\n",{"type":43,"tag":505,"props":2204,"children":2205},{"class":507,"line":858},[2206],{"type":43,"tag":505,"props":2207,"children":2208},{},[2209],{"type":49,"value":2210},"public class MyViewModel(IDataService dataService) { }\n",{"type":43,"tag":1130,"props":2212,"children":2214},{"id":2213},"_5-missing-platform-in-conditional-registration",[2215],{"type":49,"value":2216},"5. Missing Platform in Conditional Registration",{"type":43,"tag":52,"props":2218,"children":2219},{},[2220,2222,2227,2229,2235,2237,2242,2244,2250],{"type":49,"value":2221},"Forgetting a platform in ",{"type":43,"tag":58,"props":2223,"children":2225},{"className":2224},[],[2226],{"type":49,"value":152},{"type":49,"value":2228}," blocks means ",{"type":43,"tag":58,"props":2230,"children":2232},{"className":2231},[],[2233],{"type":49,"value":2234},"GetService\u003CT>()",{"type":49,"value":2236}," returns ",{"type":43,"tag":58,"props":2238,"children":2240},{"className":2239},[],[2241],{"type":49,"value":633},{"type":49,"value":2243}," at runtime on that platform. Always include an ",{"type":43,"tag":58,"props":2245,"children":2247},{"className":2246},[],[2248],{"type":49,"value":2249},"#else",{"type":49,"value":2251}," fallback or cover every target.",{"type":43,"tag":1130,"props":2253,"children":2255},{"id":2254},"_6-addscoped-without-manual-scope",[2256],{"type":49,"value":2257},"6. AddScoped Without Manual Scope",{"type":43,"tag":52,"props":2259,"children":2260},{},[2261,2263,2268,2270,2275,2277,2282,2284,2289],{"type":49,"value":2262},"See the rule table above: ",{"type":43,"tag":58,"props":2264,"children":2266},{"className":2265},[],[2267],{"type":49,"value":131},{"type":49,"value":2269}," gives you either window lifetime or Singleton behaviour, never per-navigation freshness. Use ",{"type":43,"tag":58,"props":2271,"children":2273},{"className":2272},[],[2274],{"type":49,"value":123},{"type":49,"value":2276}," or ",{"type":43,"tag":58,"props":2278,"children":2280},{"className":2279},[],[2281],{"type":49,"value":115},{"type":49,"value":2283}," unless you explicitly create and manage an ",{"type":43,"tag":58,"props":2285,"children":2287},{"className":2286},[],[2288],{"type":49,"value":369},{"type":49,"value":610},{"type":43,"tag":637,"props":2291,"children":2292},{},[],{"type":43,"tag":83,"props":2294,"children":2296},{"id":2295},"checklist",[2297],{"type":49,"value":2298},"Checklist",{"type":43,"tag":90,"props":2300,"children":2303},{"className":2301},[2302],"contains-task-list",[2304,2321,2342,2351,2360,2376,2392],{"type":43,"tag":94,"props":2305,"children":2308},{"className":2306},[2307],"task-list-item",[2309,2314,2316],{"type":43,"tag":2310,"props":2311,"children":2313},"input",{"disabled":862,"type":2312},"checkbox",[],{"type":49,"value":2315}," Every Page and ViewModel that needs injection is registered in ",{"type":43,"tag":58,"props":2317,"children":2319},{"className":2318},[],[2320],{"type":49,"value":104},{"type":43,"tag":94,"props":2322,"children":2324},{"className":2323},[2307],[2325,2328,2330,2335,2337],{"type":43,"tag":2310,"props":2326,"children":2327},{"disabled":862,"type":2312},[],{"type":49,"value":2329}," Pages and ViewModels use ",{"type":43,"tag":58,"props":2331,"children":2333},{"className":2332},[],[2334],{"type":49,"value":123},{"type":49,"value":2336},"; shared services use ",{"type":43,"tag":58,"props":2338,"children":2340},{"className":2339},[],[2341],{"type":49,"value":115},{"type":43,"tag":94,"props":2343,"children":2345},{"className":2344},[2307],[2346,2349],{"type":43,"tag":2310,"props":2347,"children":2348},{"disabled":862,"type":2312},[],{"type":49,"value":2350}," Constructor injection used everywhere possible; service locator only as last resort",{"type":43,"tag":94,"props":2352,"children":2354},{"className":2353},[2307],[2355,2358],{"type":43,"tag":2310,"props":2356,"children":2357},{"disabled":862,"type":2312},[],{"type":49,"value":2359}," Interfaces defined for services that need test substitution",{"type":43,"tag":94,"props":2361,"children":2363},{"className":2362},[2307],[2364,2367,2369,2374],{"type":43,"tag":2310,"props":2365,"children":2366},{"disabled":862,"type":2312},[],{"type":49,"value":2368}," Platform-specific ",{"type":43,"tag":58,"props":2370,"children":2372},{"className":2371},[],[2373],{"type":49,"value":152},{"type":49,"value":2375}," registrations cover all target platforms or include a fallback",{"type":43,"tag":94,"props":2377,"children":2379},{"className":2378},[2307],[2380,2383,2385,2390],{"type":43,"tag":2310,"props":2381,"children":2382},{"disabled":862,"type":2312},[],{"type":49,"value":2384}," Service-dependent work deferred to ",{"type":43,"tag":58,"props":2386,"children":2388},{"className":2387},[],[2389],{"type":49,"value":2015},{"type":49,"value":2391},", not run during XAML parse",{"type":43,"tag":94,"props":2393,"children":2395},{"className":2394},[2307],[2396,2399,2401,2406,2408],{"type":43,"tag":2310,"props":2397,"children":2398},{"disabled":862,"type":2312},[],{"type":49,"value":2400}," ",{"type":43,"tag":58,"props":2402,"children":2404},{"className":2403},[],[2405],{"type":49,"value":131},{"type":49,"value":2407}," used only when window lifetime is intended, or alongside a manually created ",{"type":43,"tag":58,"props":2409,"children":2411},{"className":2410},[],[2412],{"type":49,"value":369},{"type":43,"tag":83,"props":2414,"children":2416},{"id":2415},"references",[2417],{"type":49,"value":2418},"References",{"type":43,"tag":90,"props":2420,"children":2421},{},[2422,2434],{"type":43,"tag":94,"props":2423,"children":2424},{},[2425],{"type":43,"tag":2426,"props":2427,"children":2431},"a",{"href":2428,"rel":2429},"https:\u002F\u002Flearn.microsoft.com\u002Fdotnet\u002Fmaui\u002Ffundamentals\u002Fdependency-injection",[2430],"nofollow",[2432],{"type":49,"value":2433},"Dependency injection in .NET MAUI",{"type":43,"tag":94,"props":2435,"children":2436},{},[2437],{"type":43,"tag":2426,"props":2438,"children":2441},{"href":2439,"rel":2440},"https:\u002F\u002Flearn.microsoft.com\u002Fdotnet\u002Fcore\u002Fextensions\u002Fdependency-injection",[2430],[2442],{"type":49,"value":2443},".NET dependency injection fundamentals",{"type":43,"tag":2445,"props":2446,"children":2447},"style",{},[2448],{"type":49,"value":2449},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2451,"total":2559},[2452,2469,2484,2502,2516,2535,2545],{"slug":2453,"name":2453,"fn":2454,"description":2455,"org":2456,"tags":2457,"stars":25,"repoUrl":26,"updatedAt":2468},"analyzing-dotnet-performance","analyze .NET code for performance anti-patterns","Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I\u002FO with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2458,2459,2462,2465],{"name":20,"slug":21,"type":15},{"name":2460,"slug":2461,"type":15},"Code Analysis","code-analysis",{"name":2463,"slug":2464,"type":15},"Debugging","debugging",{"name":2466,"slug":2467,"type":15},"Performance","performance","2026-07-12T08:23:25.400375",{"slug":2470,"name":2470,"fn":2471,"description":2472,"org":2473,"tags":2474,"stars":25,"repoUrl":26,"updatedAt":2483},"android-tombstone-symbolication","symbolicate .NET runtime frames in Android tombstones","Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java\u002FKotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2475,2476,2479,2480],{"name":20,"slug":21,"type":15},{"name":2477,"slug":2478,"type":15},"Android","android",{"name":2463,"slug":2464,"type":15},{"name":2481,"slug":2482,"type":15},"Microsoft","microsoft","2026-07-12T08:23:21.595572",{"slug":2485,"name":2485,"fn":2486,"description":2487,"org":2488,"tags":2489,"stars":25,"repoUrl":26,"updatedAt":2501},"apple-crash-symbolication","symbolicate .NET runtime frames in crash logs","Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift\u002FObjective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2490,2491,2492,2495,2498],{"name":20,"slug":21,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2493,"slug":2494,"type":15},"iOS","ios",{"name":2496,"slug":2497,"type":15},"macOS","macos",{"name":2499,"slug":2500,"type":15},"Observability","observability","2026-07-12T08:23:20.369986",{"slug":2503,"name":2503,"fn":2504,"description":2505,"org":2506,"tags":2507,"stars":25,"repoUrl":26,"updatedAt":2515},"assertion-quality","evaluate assertion quality in test suites","Analyzes the variety and depth of assertions across test suites in any language. Use when the user asks to evaluate assertion quality, find shallow tests, identify assertion-free tests (no assertions or only trivial ones like Assert.IsNotNull \u002F toBeTruthy()), flag self-referential or tautological assertions, measure assertion diversity, or audit whether tests verify different facets of behavior. Polyglot: .NET, Python, TS\u002FJS, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. DO NOT USE FOR: writing new tests (use code-testing-agent \u002F writing-mstest-tests), mutation reasoning about whether tests would catch a bug (use test-gap-analysis), or a general severity-ranked anti-pattern audit (use test-anti-patterns), fixing or rewriting assertions, or writing, fixing, or modernizing MSTest tests, assertions, or attributes (use writing-mstest-tests).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2508,2509,2512],{"name":2460,"slug":2461,"type":15},{"name":2510,"slug":2511,"type":15},"QA","qa",{"name":2513,"slug":2514,"type":15},"Testing","testing","2026-07-12T08:23:51.277743",{"slug":2517,"name":2517,"fn":2518,"description":2519,"org":2520,"tags":2521,"stars":25,"repoUrl":26,"updatedAt":2534},"author-component","create and review Blazor components","Create or review Blazor components (.razor files) with correct architecture. USE FOR: writing new Blazor components that do NOT involve JavaScript interop, implementing parameters and EventCallback, RenderFragment slots, component lifecycle (OnInitializedAsync, OnParametersSet), async patterns, IAsyncDisposable, CancellationToken, CSS isolation, code-behind. DO NOT USE FOR: creating new projects (use create-blazor-project), JavaScript interop or calling browser APIs from Blazor (use use-js-interop), forms and validation (use collect-user-input), prerendering issues (use support-prerendering), HTTP data fetching patterns (use fetch-and-send-data), coordinating state between unrelated components (use coordinate-components).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2522,2523,2526,2528,2531],{"name":20,"slug":21,"type":15},{"name":2524,"slug":2525,"type":15},"Blazor","blazor",{"name":2527,"slug":498,"type":15},"C#",{"name":2529,"slug":2530,"type":15},"UI Components","ui-components",{"name":2532,"slug":2533,"type":15},"Web Development","web-development","2026-07-15T06:03:29.216359",{"slug":2536,"name":2536,"fn":2537,"description":2538,"org":2539,"tags":2540,"stars":25,"repoUrl":26,"updatedAt":2544},"binlog-failure-analysis","analyze MSBuild binary logs","Analyze MSBuild binary logs to diagnose build failures. USE FOR: build errors that are unclear from console output, diagnosing cascading failures across multi-project builds, tracing MSBuild target execution order, and generally any MSBuild build issues. Requires an existing .binlog file. DO NOT USE FOR: generating binlogs (use binlog-generation), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2541,2542,2543],{"name":2460,"slug":2461,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2481,"slug":2482,"type":15},"2026-07-12T08:21:34.637923",{"slug":2546,"name":2546,"fn":2547,"description":2548,"org":2549,"tags":2550,"stars":25,"repoUrl":26,"updatedAt":2558},"binlog-generation","generate MSBuild binary logs for diagnostics","Generate MSBuild binary logs (binlogs) for build diagnostics and analysis. USE FOR: adding \u002Fbl:{} to any dotnet build, test, pack, publish, or restore command to capture a full build execution trace, prerequisite for binlog-failure-analysis and build-perf-diagnostics skills, enabling post-build investigation of errors or performance. Requires MSBuild 17.8+ \u002F .NET 8 SDK+ for {} placeholder; PowerShell needs -bl:{{}}. DO NOT USE FOR: non-MSBuild build systems (npm, Maven, CMake), analyzing an existing binlog (use binlog-failure-analysis instead).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2551,2554,2555],{"name":2552,"slug":2553,"type":15},"Build","build",{"name":2463,"slug":2464,"type":15},{"name":2556,"slug":2557,"type":15},"Engineering","engineering","2026-07-19T05:38:19.340791",96,{"items":2561,"total":2666},[2562,2574,2581,2588,2596,2602,2610,2616,2622,2632,2645,2656],{"slug":2563,"name":2563,"fn":2564,"description":2565,"org":2566,"tags":2567,"stars":2571,"repoUrl":2572,"updatedAt":2573},"multithreaded-task-migration","migrate MSBuild tasks to multithreaded mode","Guide for migrating MSBuild tasks to multithreaded mode support, including compatibility red-team review. Use this when converting tasks to thread-safe versions, implementing IMultiThreadableTask, adding TaskEnvironment support, or auditing migrations for behavioral compatibility.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2568,2569,2570],{"name":20,"slug":21,"type":15},{"name":2556,"slug":2557,"type":15},{"name":2466,"slug":2467,"type":15},5535,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fmsbuild","2026-07-22T05:37:33.965588",{"slug":2453,"name":2453,"fn":2454,"description":2455,"org":2575,"tags":2576,"stars":25,"repoUrl":26,"updatedAt":2468},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2577,2578,2579,2580],{"name":20,"slug":21,"type":15},{"name":2460,"slug":2461,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2466,"slug":2467,"type":15},{"slug":2470,"name":2470,"fn":2471,"description":2472,"org":2582,"tags":2583,"stars":25,"repoUrl":26,"updatedAt":2483},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2584,2585,2586,2587],{"name":20,"slug":21,"type":15},{"name":2477,"slug":2478,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2481,"slug":2482,"type":15},{"slug":2485,"name":2485,"fn":2486,"description":2487,"org":2589,"tags":2590,"stars":25,"repoUrl":26,"updatedAt":2501},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2591,2592,2593,2594,2595],{"name":20,"slug":21,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2493,"slug":2494,"type":15},{"name":2496,"slug":2497,"type":15},{"name":2499,"slug":2500,"type":15},{"slug":2503,"name":2503,"fn":2504,"description":2505,"org":2597,"tags":2598,"stars":25,"repoUrl":26,"updatedAt":2515},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2599,2600,2601],{"name":2460,"slug":2461,"type":15},{"name":2510,"slug":2511,"type":15},{"name":2513,"slug":2514,"type":15},{"slug":2517,"name":2517,"fn":2518,"description":2519,"org":2603,"tags":2604,"stars":25,"repoUrl":26,"updatedAt":2534},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2605,2606,2607,2608,2609],{"name":20,"slug":21,"type":15},{"name":2524,"slug":2525,"type":15},{"name":2527,"slug":498,"type":15},{"name":2529,"slug":2530,"type":15},{"name":2532,"slug":2533,"type":15},{"slug":2536,"name":2536,"fn":2537,"description":2538,"org":2611,"tags":2612,"stars":25,"repoUrl":26,"updatedAt":2544},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2613,2614,2615],{"name":2460,"slug":2461,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2481,"slug":2482,"type":15},{"slug":2546,"name":2546,"fn":2547,"description":2548,"org":2617,"tags":2618,"stars":25,"repoUrl":26,"updatedAt":2558},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2619,2620,2621],{"name":2552,"slug":2553,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2556,"slug":2557,"type":15},{"slug":2623,"name":2623,"fn":2624,"description":2625,"org":2626,"tags":2627,"stars":25,"repoUrl":26,"updatedAt":2631},"build-parallelism","optimize MSBuild build parallelism","Diagnose and fix under-parallelized MSBuild builds. USE WHEN a multi-project solution build is slower than expected, doesn't speed up when you add cores, pegs a single core while others idle, or you want to know why `-m` isn't helping. Note: `\u002Fmaxcpucount` default is 1 (sequential) — always pass `-m` for parallel builds. Covers finding the critical path (longest serial ProjectReference chain), graph build (`\u002Fgraph`), BuildInParallel, and solution filters (`.slnf`). DO NOT USE FOR: single-project builds, incremental issues (use incremental-build), compilation slowness inside one project (use build-perf-diagnostics), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2628,2629,2630],{"name":20,"slug":21,"type":15},{"name":2556,"slug":2557,"type":15},{"name":2466,"slug":2467,"type":15},"2026-07-19T05:38:18.364937",{"slug":2633,"name":2633,"fn":2634,"description":2635,"org":2636,"tags":2637,"stars":25,"repoUrl":26,"updatedAt":2644},"build-perf-baseline","establish and optimize build performance baselines","Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before\u002Fafter measurements (cold, warm, no-op scenarios), applying optimization strategies like MSBuild Server, static graph builds, artifacts output, and dependency graph trimming. Start here before diving into build-perf-diagnostics, incremental-build, or build-parallelism. DO NOT USE FOR: non-MSBuild build systems, detailed bottleneck analysis (use build-perf-diagnostics after baselining).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2638,2639,2642,2643],{"name":2556,"slug":2557,"type":15},{"name":2640,"slug":2641,"type":15},"Monitoring","monitoring",{"name":2466,"slug":2467,"type":15},{"name":2513,"slug":2514,"type":15},"2026-07-12T08:21:35.865649",{"slug":2646,"name":2646,"fn":2647,"description":2648,"org":2649,"tags":2650,"stars":25,"repoUrl":26,"updatedAt":2655},"build-perf-diagnostics","diagnose MSBuild build performance bottlenecks","Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target\u002FTask Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2651,2652,2653,2654],{"name":20,"slug":21,"type":15},{"name":2463,"slug":2464,"type":15},{"name":2556,"slug":2557,"type":15},{"name":2466,"slug":2467,"type":15},"2026-07-12T08:21:40.961722",{"slug":2657,"name":2657,"fn":2658,"description":2659,"org":2660,"tags":2661,"stars":25,"repoUrl":26,"updatedAt":2665},"check-bin-obj-clash","detect MSBuild output path conflicts","Detects MSBuild projects with conflicting OutputPath or IntermediateOutputPath. USE FOR: builds failing with 'Cannot create a file when that file already exists', 'The process cannot access the file because it is being used by another process', intermittent build failures that succeed on retry, or missing\u002Foverwritten outputs in multi-project or multi-targeting builds where bin\u002Fobj (or project.assets.json) collide. Common causes: shared OutputPath, missing AppendTargetFrameworkToOutputPath, extra global properties (e.g. PublishReadyToRun), or SetTargetFramework on a ProjectReference to a single-targeting project. DO NOT USE FOR: file access errors unrelated to MSBuild (OS-level locking), single-project single-TFM builds, non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2662,2663,2664],{"name":2463,"slug":2464,"type":15},{"name":2556,"slug":2557,"type":15},{"name":2510,"slug":2511,"type":15},"2026-07-19T05:38:14.336279",144]