Skip to content

AddNavigationProvider<T>() registers a Scoped provider consumed by a Transient/Singleton IUndoRedoService, so DI scope validation throws for both documented setups #109

Description

@matt-edmondson

What's wrong

AddNavigationProvider<TNavigationProvider>() (UndoRedo/ServiceCollectionExtensions.cs ~L83-89) uses TryAddScoped<INavigationProvider, TNavigationProvider>(). But UndoRedoService, which takes INavigationProvider? in its constructor, is registered Transient by AddUndoRedo() (~L34) and Singleton by AddSingletonUndoRedo() (~L76). A scoped dependency of a singleton (or of a transient resolved from the root) is a captive dependency, which Microsoft.Extensions.DependencyInjection rejects under ValidateScopes/ValidateOnBuild — both on by default in the generic host's Development environment.

Failure scenarios (reproduced with temporary MSTest tests)

  • AddSingletonUndoRedo() + AddNavigationProvider<Nav>() (the natural desktop-app setup):
    BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true, ValidateScopes = true }) throws
    AggregateException ... Cannot consume scoped service 'ktsu.UndoRedo.INavigationProvider' from singleton 'ktsu.UndoRedo.Contracts.IUndoRedoService'.
    Without validation, the singleton silently captures a provider resolved from the root scope, so "scoped" never actually applies.
  • AddUndoRedo() + AddNavigationProvider<Nav>() (what docs/dependency-injection.md ~L123-126 recommends "for most applications"):
    resolving IUndoRedoService from the root provider with ValidateScopes = true throws
    InvalidOperationException: Cannot resolve 'ktsu.UndoRedo.Contracts.IUndoRedoService' from root provider because it requires scoped service 'ktsu.UndoRedo.INavigationProvider'.

So a consumer following the docs gets a startup crash under Host.CreateDefaultBuilder in Development.

Suggested fix

Make the navigation provider's lifetime compatible with the undo/redo registration — e.g. change AddNavigationProvider<T> to TryAddTransient, or deprecate the scoped variant and direct users to AddSingletonNavigationProvider alongside AddSingletonUndoRedo. Update the lifetime table / "Scoped Navigation Provider" section in docs/dependency-injection.md to match.

Acceptance criteria

  • Tests build a provider with ValidateOnBuild = true, ValidateScopes = true for each supported pairing (AddUndoRedo/AddSingletonUndoRedo × each Add*NavigationProvider) and resolve IUndoRedoService from the root without exceptions — or unsupported pairings are removed/documented.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions