Skip to content

[API Proposal]: Scoped Service Provider Accessor #130169

Description

@Waleed-KH

Background and motivation

In modern .NET applications, developers heavily rely on dependency injection (DI) scopes to manage the lifetime of resources per operation (e.g., handling a web request, processing a message queue item, or executing a background task). While ASP.NET Core provides IHttpContextAccessor to access the current HTTP context and its associated RequestServices ambiently, .NET lacks a generic, runtime-agnostic mechanism to access the active IServiceProvider scope outside of web environments.

This limitation frequently causes architectural pain points in non-web applications (such as Worker Services, Console Apps, or any Generic Host), or when writing infrastructure libraries that span both web and non-web contexts. For example:

  • Cross-Lifetime Consumption: A Singleton service needs to process contextual data or interact with a stateful Scoped service, but it is invoked from deep within a scoped execution pipeline where passing IServiceProvider explicitly through method signatures is impossible or highly intrusive.
  • Library Authoring: Infrastructure components need to ambiently fetch services from the active scope without forcing application code to manually flow the container down the call stack.

Providing a built-in IScopedServiceProviderAccessor powered by AsyncLocal<T> resolves these issues. It standardizes ambient scope tracking natively within Microsoft.Extensions.DependencyInjection, bringing parity to non-web hosting environments and eliminating the need for developers to repeatedly implement custom, error-prone thread-local or async-local tracking wrappers.

API Proposal

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Provides access to the current scope <see cref="IServiceProvider"/>, if one is available.
/// </summary>
public interface IScopedServiceProviderAccessor
{
    /// <summary>
    /// Gets or sets the current scope <see cref="System.IServiceProvider"/>. Returns <see langword="null" /> if there is no active scope.
    /// </summary>
    IServiceProvider? ServiceProvider { get; set; }
}

API Usage

public class SingletonService(IScopedServiceProviderAccessor scopeAccessor)
{
    private readonly IScopedServiceProviderAccessor _scopeAccessor = scopeAccessor;

    public void DoSomething()
    {
        // Effortlessly retrieve the caller's scoped provider instance
        var scopedProvider = _scopeAccessor.ServiceProvider;

        if (scopedProvider is not null)
        {
            // Resolve scoped service specific to this call execution flow
            var currentUser = scopedProvider.GetRequiredService<UserContext>();
        }
    }
}

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions