{"id":"a0f1013f-33ed-467b-a1be-a22d3ae58d8b","shortId":"KL7AFD","kind":"skill","title":"mvvm-toolkit-di","tagline":"Wire CommunityToolkit.Mvvm ViewModels into Microsoft.Extensions.DependencyInjection. Covers the .NET Generic Host composition root, constructor injection, service lifetimes (Singleton / Transient / Scoped), IMessenger registration, resolving ViewModels in Views, keyed services, t","description":"# CommunityToolkit.Mvvm + `Microsoft.Extensions.DependencyInjection`\n\nThe MVVM Toolkit deliberately ships **no DI container** — it composes with\n`Microsoft.Extensions.DependencyInjection`, the same container ASP.NET\nCore, Worker services, and the .NET Generic Host use.\n\n> **TL;DR.** Build the service provider once at startup (prefer\n> `Host.CreateDefaultBuilder()`). Register services and ViewModels.\n> Inject through constructors. Avoid `Ioc.Default.GetService<T>()`\n> in user code.\n\n---\n\n## When to use this skill\n\n- Standing up the composition root for a new XAML app (WPF, WinUI 3,\n  MAUI, Uno, Avalonia)\n- Choosing service/VM lifetimes\n- Wiring `IMessenger` once and injecting it into `ObservableRecipient`\n  ViewModels\n- Resolving a page's ViewModel without coupling to a service locator\n- Diagnosing \"Unable to resolve service for type X while attempting to\n  activate Y\"\n\nFor source generators and ViewModel patterns see the **`mvvm-toolkit`**\nskill. For Messenger pub/sub see **`mvvm-toolkit-messenger`**.\n\n---\n\n## Recommended composition root (Generic Host)\n\n```csharp\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing CommunityToolkit.Mvvm.Messaging;\n\npublic partial class App : Application\n{\n    public IHost Host { get; }\n\n    public App()\n    {\n        Host = Microsoft.Extensions.Hosting.Host\n            .CreateDefaultBuilder()\n            .ConfigureServices((_, services) =>\n            {\n                services.AddSingleton<IFilesService, FilesService>();\n                services.AddSingleton<ISettingsService, SettingsService>();\n                services.AddSingleton<IMessenger>(WeakReferenceMessenger.Default);\n\n                services.AddSingleton<ShellViewModel>();\n                services.AddTransient<ContactViewModel>();\n                services.AddTransient<EditorViewModel>();\n            })\n            .Build();\n    }\n\n    public static T GetService<T>() where T : class =>\n        ((App)Current).Host.Services.GetRequiredService<T>();\n}\n```\n\nGeneric Host benefits:\n\n- `appsettings.json` binding via `Microsoft.Extensions.Configuration`\n- Logging via `Microsoft.Extensions.Logging`\n- Hosted services (`IHostedService`) for background work\n- Scope validation in development builds\n\n> WPF and Windows Forms must integrate the host lifetime with the app\n> lifetime — see\n> [Use the .NET Generic Host in a WPF app](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/how-to-use-host-builder).\n\n### Without Generic Host\n\nWhen you only need a service container and want zero extra dependencies:\n\n```csharp\nvar services = new ServiceCollection();\nservices.AddSingleton<IFilesService, FilesService>();\nservices.AddTransient<ContactViewModel>();\nServiceProvider provider = services.BuildServiceProvider();\n```\n\n---\n\n## Constructor injection\n\nInject services and child ViewModels through the constructor:\n\n```csharp\npublic sealed partial class ContactViewModel(\n    IFilesService files,\n    IMessenger messenger,\n    ILogger<ContactViewModel> logger)\n    : ObservableRecipient(messenger)\n{\n    [ObservableProperty]\n    private string? name;\n\n    [RelayCommand]\n    private async Task SaveAsync()\n    {\n        logger.LogInformation(\"Saving {Name}\", Name);\n        await files.SaveAsync(Name!);\n    }\n}\n```\n\nWhy constructor injection beats a service locator:\n\n- Dependencies are explicit and visible at the call site\n- Unit tests inject fakes/mocks directly\n- The DI container validates the dependency graph at startup\n- Missing registrations throw immediately, not at first use\n\n---\n\n## Lifetimes\n\n| Lifetime | Method | Typical use in XAML apps |\n|----------|--------|--------------------------|\n| Singleton | `AddSingleton<T>` | Shell/main-window VM, settings, file/HTTP services, the shared `IMessenger`, app-wide caches |\n| Transient | `AddTransient<T>` | Per-page or per-document ViewModels (a fresh instance every resolve) |\n| Scoped | `AddScoped<T>` | Rarely needed in client apps; useful with explicit `IServiceScope` (e.g., per-window scopes) |\n\n```csharp\nservices.AddSingleton<ShellViewModel>();   // 1 instance for app lifetime\nservices.AddTransient<NoteViewModel>();    // new instance per resolve\nservices.AddScoped<DialogService>();       // 1 per scope (rare)\n```\n\n---\n\n## Resolving in a View\n\nResolve the page's root ViewModel in code-behind, then let it pull its\nown dependencies:\n\n```csharp\npublic sealed partial class ContactPage : Page\n{\n    public ContactViewModel ViewModel { get; }\n\n    public ContactPage()\n    {\n        ViewModel = App.GetService<ContactViewModel>();\n        InitializeComponent();\n    }\n}\n```\n\nBind in XAML with `{x:Bind ViewModel.Xxx}` (compiled bindings) or\n`{Binding Xxx}` against `DataContext`.\n\nFor navigation frameworks (WinUI 3 `Frame.Navigate`, MAUI Shell, Prism,\nMVVMCross), let the framework resolve the page and the page resolves its\nViewModel from DI. Don't `new` ViewModels manually.\n\n---\n\n## `IMessenger` registration\n\nRegister the messenger you want once, inject `IMessenger` everywhere:\n\n```csharp\nservices.AddSingleton<IMessenger>(WeakReferenceMessenger.Default);\n// or\nservices.AddSingleton<IMessenger>(StrongReferenceMessenger.Default);\n```\n\nThen:\n\n```csharp\npublic sealed partial class MyViewModel(IMessenger messenger)\n    : ObservableRecipient(messenger) { }\n```\n\nFor per-window messengers, register with keyed services or as scoped\ninstances and inject into per-window ViewModels.\n\nSee the **`mvvm-toolkit-messenger`** skill for the messenger surface area.\n\n---\n\n## Keyed services (.NET 8+)\n\nResolve different implementations of the same interface by key:\n\n```csharp\nservices.AddKeyedSingleton<IExporter, CsvExporter>(\"csv\");\nservices.AddKeyedSingleton<IExporter, JsonExporter>(\"json\");\n\npublic sealed partial class ExportViewModel(\n    [FromKeyedServices(\"csv\")] IExporter csvExporter,\n    [FromKeyedServices(\"json\")] IExporter jsonExporter)\n    : ObservableObject { /* ... */ }\n```\n\n---\n\n## Testing seams\n\nConstructor-injected dependencies are trivial to swap in tests. With\n`Moq`:\n\n```csharp\n[Fact]\npublic async Task Save_calls_files_service()\n{\n    var files = new Mock<IFilesService>();\n    var messenger = new WeakReferenceMessenger();\n    var logger = NullLogger<ContactViewModel>.Instance;\n\n    var vm = new ContactViewModel(files.Object, messenger, logger)\n    {\n        Name = \"Ada\"\n    };\n\n    await vm.SaveCommand.ExecuteAsync(null);\n\n    files.Verify(f => f.SaveAsync(\"Ada\"), Times.Once);\n}\n```\n\nIf you're mocking `Ioc.Default` or static state, the ViewModel is using a\nservice locator — refactor to constructor injection.\n\n---\n\n## Legacy: `Ioc.Default`\n\n`CommunityToolkit.Mvvm.DependencyInjection.Ioc` is an escape hatch for\ncases where constructor injection is impossible — XAML-instantiated VMs\nfor design-time data, `ValueConverter`s, control templates.\n\n```csharp\nIoc.Default.ConfigureServices(\n    new ServiceCollection()\n        .AddSingleton<IFilesService, FilesService>()\n        .AddTransient<ContactViewModel>()\n        .BuildServiceProvider());\n\nvar files = Ioc.Default.GetRequiredService<IFilesService>();\n```\n\nTreat it as the last resort. Inside ViewModels, services, and any class\nthe DI container can construct, prefer constructor injection.\n\n---\n\n## Common pitfalls\n\n1. **`Ioc.Default.GetService<T>()` inside a VM constructor.** Hides the\n   dependency, breaks unit tests, prevents startup graph validation.\n2. **Everything `Singleton`.** A \"per-document\" VM registered as singleton\n   becomes shared state across all documents — subtle data corruption.\n   Use `AddTransient` for per-instance VMs.\n3. **Multiple `BuildServiceProvider()` calls.** Each call is a fresh\n   container — singletons aren't shared. Build once at startup.\n4. **Capturing `IServiceProvider` in long-lived objects.** Indicates a\n   service-locator pattern. Inject the specific dependencies you need.\n5. **No scope validation in development.** Use `Host.CreateDefaultBuilder()`\n   (which sets `ValidateScopes` and `ValidateOnBuild` in development) so\n   registration mistakes fail at startup, not at first use.\n6. **Resolving scoped services from the root provider.** They're\n   effectively promoted to singleton lifetime — the warning is silent\n   without scope validation. Either change the lifetime or resolve from\n   an explicit `IServiceScope`.\n\n---\n\n## References\n\n| Topic | File |\n|-------|------|\n| Full deep dive (Generic Host setup, lifetimes, keyed services, testing patterns, legacy Ioc) | [`references/dependency-injection.md`](references/dependency-injection.md) |\n\nExternal:\n\n- DI overview: <https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection>\n- DI usage: <https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage>\n- MVVM Toolkit Ioc page: <https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ioc>\n- Generic Host: <https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host>","tags":["mvvm","toolkit","awesome","copilot","github","agent-skills","agents","custom-agents","github-copilot","hacktoberfest","prompt-engineering"],"capabilities":["skill","source-github","skill-mvvm-toolkit-di","topic-agent-skills","topic-agents","topic-awesome","topic-custom-agents","topic-github-copilot","topic-hacktoberfest","topic-prompt-engineering"],"categories":["awesome-copilot"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/github/awesome-copilot/mvvm-toolkit-di","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add github/awesome-copilot","source_repo":"https://github.com/github/awesome-copilot","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 33270 github stars · SKILL.md body (8,916 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:52:17.963Z","embedding":null,"createdAt":"2026-05-11T06:52:41.182Z","updatedAt":"2026-05-18T18:52:17.963Z","lastSeenAt":"2026-05-18T18:52:17.963Z","tsv":"'/en-us/dotnet/communitytoolkit/mvvm/ioc':913 '/en-us/dotnet/core/extensions/dependency-injection':901 '/en-us/dotnet/core/extensions/dependency-injection-usage':906 '/en-us/dotnet/core/extensions/generic-host':918 '/en-us/dotnet/desktop/wpf/app-development/how-to-use-host-builder).':256 '1':417,428,740 '2':756 '3':100,487,783 '4':801 '5':821 '6':846 '8':575 'across':770 'activ':138 'ada':651,658 'addscop':400 'addsingleton':371,710 'addtransi':385,713,777 'app':97,175,182,207,242,253,369,381,405,420 'app-wid':380 'app.getservice':467 'applic':176 'appsettings.json':213 'area':571 'aren':794 'asp.net':50 'async':314,625 'attempt':136 'avalonia':103 'avoid':78 'await':321,652 'background':224 'beat':327 'becom':767 'behind':445 'benefit':212 'bind':214,469,474,477,479 'break':749 'build':62,199,230,797 'buildserviceprovid':714,785 'cach':383 'call':338,628,786,788 'captur':802 'case':687 'chang':869 'child':289 'choos':104 'class':174,206,298,457,534,597,729 'client':404 'code':82,444 'code-behind':443 'common':738 'communitytoolkit.mvvm':6,33 'communitytoolkit.mvvm.dependencyinjection.ioc':681 'communitytoolkit.mvvm.messaging':171 'compil':476 'compos':44 'composit':15,91,161 'configureservic':186 'construct':734 'constructor':17,77,284,293,325,611,677,689,736,745 'constructor-inject':610 'contactpag':458,465 'contactviewmodel':299,461,646 'contain':42,49,266,347,732,792 'control':704 'core':51 'corrupt':775 'coupl':122 'cover':10 'createdefaultbuild':185 'csharp':165,272,294,415,453,523,530,585,622,706 'csv':589,600 'csvexport':588,602 'current':208 'data':701,774 'datacontext':482 'deep':882 'deliber':38 'depend':271,331,350,452,613,748,818 'design':699 'design-tim':698 'develop':229,826,835 'di':4,41,346,506,731,897,902 'diagnos':127 'differ':577 'direct':344 'dive':883 'document':392,762,772 'dr':61 'e.g':410 'effect':856 'either':868 'escap':684 'everi':397 'everyth':757 'everywher':522 'explicit':333,408,876 'exportviewmodel':598 'extern':896 'extra':270 'f':656 'f.saveasync':657 'fact':623 'fail':839 'fakes/mocks':343 'file':301,629,632,716,880 'file/http':375 'files.object':647 'files.saveasync':322 'files.verify':655 'filesservic':190,279,712 'first':360,844 'form':234 'frame.navigate':488 'framework':485,495 'fresh':395,791 'fromkeyedservic':599,603 'full':881 'generat':142 'generic':13,57,163,210,248,258,884,914 'get':180,463 'getservic':203 'graph':351,754 'hatch':685 'hide':746 'host':14,58,164,179,183,211,220,238,249,259,885,915 'host.createdefaultbuilder':70,828 'host.services.getrequiredservice':209 'iexport':587,591,601,605 'ifilesservic':189,278,300,711 'ihost':178 'ihostedservic':222 'ilogg':304 'imesseng':24,108,302,379,512,521,536 'immedi':357 'implement':578 'imposs':692 'indic':809 'initializecompon':468 'inject':18,75,111,285,286,326,342,520,554,612,678,690,737,815 'insid':724,742 'instanc':396,418,424,552,642,781 'instanti':695 'integr':236 'interfac':582 'ioc':893,909 'ioc.default':664,680 'ioc.default.configureservices':707 'ioc.default.getrequiredservice':717 'ioc.default.getservice':79,741 'iserviceprovid':803 'iservicescop':409,877 'isettingsservic':192 'json':593,604 'jsonexport':592,606 'key':30,547,572,584,888 'last':722 'learn.microsoft.com':255,900,905,912,917 'learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ioc':911 'learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection':899 'learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage':904 'learn.microsoft.com/en-us/dotnet/core/extensions/generic-host':916 'learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/how-to-use-host-builder).':254 'legaci':679,892 'let':447,493 'lifetim':20,106,239,243,362,363,421,860,871,887 'live':807 'locat':126,330,674,813 'log':217 'logger':305,640,649 'logger.loginformation':317 'long':806 'long-liv':805 'manual':511 'maui':101,489 'messeng':153,159,303,307,516,537,539,544,565,569,636,648 'method':364 'microsoft.extensions.configuration':216 'microsoft.extensions.dependencyinjection':9,34,46,167 'microsoft.extensions.hosting':169 'microsoft.extensions.hosting.host':184 'microsoft.extensions.logging':219 'miss':354 'mistak':838 'mock':634,663 'moq':621 'multipl':784 'must':235 'mvvm':2,36,149,157,563,907 'mvvm-toolkit':148 'mvvm-toolkit-di':1 'mvvm-toolkit-messeng':156,562 'mvvmcross':492 'myviewmodel':535 'name':311,319,320,323,650 'navig':484 'need':263,402,820 'net':12,56,247,574 'new':95,275,423,509,633,637,645,708 'null':654 'nulllogg':641 'object':808 'observableobject':607 'observableproperti':308 'observablerecipi':114,306,538 'overview':898 'page':118,388,438,459,498,501,910 'partial':173,297,456,533,596 'pattern':145,814,891 'per':387,391,412,425,429,542,557,761,780 'per-docu':390,760 'per-inst':779 'per-pag':386 'per-window':411,541,556 'pitfal':739 'prefer':69,735 'prevent':752 'prism':491 'privat':309,313 'promot':857 'provid':65,282,853 'pub/sub':154 'public':172,177,181,200,295,454,460,464,531,594,624 'pull':449 'rare':401,431 're':662,855 'recommend':160 'refactor':675 'refer':878 'references/dependency-injection.md':894,895 'regist':71,514,545,764 'registr':25,355,513,837 'relaycommand':312 'resolv':26,116,130,398,426,432,436,496,502,576,847,873 'resort':723 'root':16,92,162,440,852 'save':318,627 'saveasync':316 'scope':23,226,399,414,430,551,823,848,866 'seal':296,455,532,595 'seam':609 'see':146,155,244,560 'servic':19,31,53,64,72,125,131,187,221,265,274,287,329,376,548,573,630,673,726,812,849,889 'service-loc':811 'service/vm':105 'servicecollect':276,709 'serviceprovid':281 'services.addkeyedsingleton':586,590 'services.addscoped':427 'services.addsingleton':188,191,194,196,277,416,524,527 'services.addtransient':197,198,280,422 'services.buildserviceprovider':283 'set':374,830 'settingsservic':193 'setup':886 'share':378,768,796 'shell':490 'shell/main-window':372 'ship':39 'silent':864 'singleton':21,370,758,766,793,859 'site':339 'skill':87,151,566 'skill-mvvm-toolkit-di' 'sourc':141 'source-github' 'specif':817 'stand':88 'startup':68,353,753,800,841 'state':667,769 'static':201,666 'string':310 'strongreferencemessenger.default':528 'subtl':773 'surfac':570 'swap':617 'task':315,626 'templat':705 'test':341,608,619,751,890 'throw':356 'time':700 'times.once':659 'tl':60 'toolkit':3,37,150,158,564,908 'topic':879 'topic-agent-skills' 'topic-agents' 'topic-awesome' 'topic-custom-agents' 'topic-github-copilot' 'topic-hacktoberfest' 'topic-prompt-engineering' 'transient':22,384 'treat':718 'trivial':615 'type':133 'typic':365 'unabl':128 'unit':340,750 'uno':102 'usag':903 'use':59,85,166,168,170,245,361,366,406,671,776,827,845 'user':81 'valid':227,348,755,824,867 'validateonbuild':833 'validatescop':831 'valueconvert':702 'var':273,631,635,639,643,715 'via':215,218 'view':29,435 'viewmodel':7,27,74,115,120,144,290,393,441,462,466,504,510,559,669,725 'viewmodel.xxx':475 'visibl':335 'vm':373,644,744,763 'vm.savecommand.executeasync':653 'vms':696,782 'want':268,518 'warn':862 'weakreferencemesseng':638 'weakreferencemessenger.default':195,525 'wide':382 'window':233,413,543,558 'winui':99,486 'wire':5,107 'without':121,257,865 'work':225 'worker':52 'wpf':98,231,252 'x':134,473 'xaml':96,368,471,694 'xaml-instanti':693 'xxx':480 'y':139 'zero':269","prices":[{"id":"09fea1e2-f8d0-4a09-918d-6b128fa5071b","listingId":"a0f1013f-33ed-467b-a1be-a22d3ae58d8b","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"github","category":"awesome-copilot","install_from":"skills.sh"},"createdAt":"2026-05-11T06:52:41.182Z"}],"sources":[{"listingId":"a0f1013f-33ed-467b-a1be-a22d3ae58d8b","source":"github","sourceId":"github/awesome-copilot/mvvm-toolkit-di","sourceUrl":"https://github.com/github/awesome-copilot/tree/main/skills/mvvm-toolkit-di","isPrimary":false,"firstSeenAt":"2026-05-11T06:52:41.182Z","lastSeenAt":"2026-05-18T18:52:17.963Z"}],"details":{"listingId":"a0f1013f-33ed-467b-a1be-a22d3ae58d8b","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"github","slug":"mvvm-toolkit-di","github":{"repo":"github/awesome-copilot","stars":33270,"topics":["agent-skills","agents","ai","awesome","custom-agents","github-copilot","hacktoberfest","prompt-engineering"],"license":"mit","html_url":"https://github.com/github/awesome-copilot","pushed_at":"2026-05-18T01:26:59Z","description":"Community-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.","skill_md_sha":"0b5a15f1983e6c4d6ee86e1aada20bfe71987d13","skill_md_path":"skills/mvvm-toolkit-di/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/github/awesome-copilot/tree/main/skills/mvvm-toolkit-di"},"layout":"multi","source":"github","category":"awesome-copilot","frontmatter":{"name":"mvvm-toolkit-di","description":"Wire CommunityToolkit.Mvvm ViewModels into Microsoft.Extensions.DependencyInjection. Covers the .NET Generic Host composition root, constructor injection, service lifetimes (Singleton / Transient / Scoped), IMessenger registration, resolving ViewModels in Views, keyed services, testing seams, and the legacy Ioc.Default escape hatch. Use across WPF, WinUI 3, .NET MAUI, Uno, and Avalonia."},"skills_sh_url":"https://skills.sh/github/awesome-copilot/mvvm-toolkit-di"},"updatedAt":"2026-05-18T18:52:17.963Z"}}