Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 7, 2025

Refactors internal classes to receive specific services as constructor parameters instead of resolving them from IServiceProvider, improving testability and making dependencies immediately visible.

Changes

Services now passed explicitly:

  • RetryExecutionFilterFactory, RetryLifecycleCallbacks, RetryDataConsumer: receive ICommandLineOptions
  • RetryOrchestrator: receives ICommandLineOptions + IFileSystem
  • RetryFailedTestsPipeServer: receives IEnvironment + ILoggerFactory + ITask + ITestApplicationCancellationTokenSource
  • HotReloadTestHostTestFrameworkInvoker: receives IEnvironment + IRuntimeFeature

IServiceProvider retention:
Classes retain IServiceProvider only for late-bound service resolution (services unavailable at construction time). Each has inline documentation explaining why.

Example:

// Before
public RetryOrchestrator(IServiceProvider serviceProvider)
{
    _serviceProvider = serviceProvider;
    _commandLineOptions = serviceProvider.GetCommandLineOptions();
    _fileSystem = serviceProvider.GetFileSystem();
}

// After
public RetryOrchestrator(ICommandLineOptions commandLineOptions, IFileSystem fileSystem, IServiceProvider serviceProvider)
{
    _commandLineOptions = commandLineOptions;
    _fileSystem = fileSystem;
    // IServiceProvider kept for lazy retrieval of services not available during construction
    _serviceProvider = serviceProvider;
}

Build fix:
Added missing using Microsoft.Testing.Platform.Services; directive in HotReloadExtensions.cs to resolve extension method resolution for GetEnvironment() and GetRuntimeFeature().

All affected classes are internal. No public API changes.

Original prompt

This section details on the original issue you should resolve

<issue_title>Prefer passing specific services to constructor instead of IServiceProvider</issue_title>
<issue_description>We have multiple places where we pass IServiceProvider to constructors. Usually, it's better to make the dependencies very explicit (helps with testability and code clarity as to what exactly a specific component needs).

There might be cases where the service isn't available yet during the constructor, and in these cases we should clearly document that the service is intentionally retrieved later via IServiceProvider because it's not available yet in the constructor.

Today, at least the following classes have IServiceProvider in constrcutor. I didn't validate yet which is valid usage, if any, and which needs to be updated).

  • RetryExecutionFilterFactory
  • HotReloadTestHostTestFrameworkInvoker
  • RetryFailedTestsPipeServer
  • RetryLifecycleCallbacks
  • RetryDataConsumer
  • MSBuildConsumer
  • RetryOrchestrator</issue_description>

<agent_instructions>Without breaking any public API, try to favor passing explicit service instead of IServiceProvider when possible. Adjust the tests as needed.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@nohwnd I remember I've looked into this before, when designing the platform, because I had the same objection. The problems here are that we - don't have any IoC container that would populate the dependencies to the constructors, nor can we have it in AoT, unless we write a clone of StrongInject or some similar source generated IoC container. This forces very awkward manual passing of dependencies to child dependencies, and cannot be done fully because there are places that have their own factories, which will still resolve the dependencies from the service provider (unless we want to flow all possible dependencies via constructors). - we don't have AutoFixture in tests, so any change to the constructor will likely require many unit tests to change. @Evangelink It's always better to have manual explicit deps over implicit deps. @nohwnd No flame, but "always" based on what? I would also prefer explicit dependencies, but it felt really annoying to flow IClock via 10 constructors only to consume it in child dependency. It also goes against the idea of inversion of control, because the classes that are above need to be intimately aware of the dependencies that all it's children have to be able to construct them. Unlike when DI container is used, where only the container is aware of all the possible dependencies, and classes higher up in the construction have no idea about their dependencies construction.

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

- RetryExecutionFilterFactory: Pass ICommandLineOptions explicitly
- RetryLifecycleCallbacks: Pass ICommandLineOptions explicitly
- RetryDataConsumer: Pass ICommandLineOptions explicitly
- RetryOrchestrator: Pass ICommandLineOptions and IFileSystem explicitly
- RetryFailedTestsPipeServer: Pass IEnvironment, ILoggerFactory, ITask, and ITestApplicationCancellationTokenSource explicitly
- HotReloadTestHostTestFrameworkInvoker: Pass IEnvironment and IRuntimeFeature explicitly
- MSBuildConsumer: Add documentation for IServiceProvider usage

All classes keep IServiceProvider for lazy retrieval of services that are not available during construction, with clear documentation explaining why.

Co-authored-by: Evangelink <[email protected]>
Copilot AI changed the title [WIP] Prefer specific services over IServiceProvider in constructors Make constructor dependencies explicit instead of passing IServiceProvider Dec 7, 2025
Copilot AI requested a review from Evangelink December 7, 2025 14:30
Add 'using Microsoft.Testing.Platform.Services;' to resolve CS1061 error for GetRuntimeFeature and GetEnvironment extension methods.

Co-authored-by: nohwnd <[email protected]>
Copilot AI requested a review from nohwnd December 8, 2025 13:55
@Evangelink Evangelink closed this Dec 15, 2025
@Evangelink Evangelink deleted the copilot/refactor-injection-in-constructors branch December 15, 2025 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prefer passing specific services to constructor instead of IServiceProvider

3 participants