From 18765cde2fba61a72b2757ddbb65851644978d12 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:49:02 +0000 Subject: [PATCH 1/4] Initial plan From 936e4345d768506c7bb86f840ffe85bfc23f71d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:04:21 +0000 Subject: [PATCH 2/4] docs: add XML docs for DI Dispose/DisposeAsync explaining async-only disposable behavior Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com> --- .../src/AsyncServiceScope.cs | 23 +++++++++++++++++-- .../src/IServiceScope.cs | 12 +++++++++- .../src/ServiceProvider.cs | 22 ++++++++++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs index 230ead2bd3eed9..82cded2d4883ac 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs @@ -30,13 +30,32 @@ public AsyncServiceScope(IServiceScope serviceScope) /// public IServiceProvider ServiceProvider => _serviceScope.ServiceProvider; - /// + /// + /// Ends the scope lifetime and disposes all resolved services. + /// + /// + /// Prefer calling over this method. If any resolved service implements + /// but not , this method throws an + /// (or an if multiple such + /// services are resolved). Use to properly handle all disposable services, + /// or explicitly perform sync-over-async on the caller side if synchronous disposal is required. + /// + /// A resolved service implements but not . + /// Multiple resolved services implement but not . public void Dispose() { _serviceScope.Dispose(); } - /// + /// + /// Asynchronously ends the scope lifetime and disposes all resolved services that implement or . + /// + /// + /// This is the preferred disposal method. When the underlying scope implements , + /// this method handles services that implement only without throwing. + /// When it does not, this method falls back to calling . + /// + /// A value task that represents the asynchronous operation. public ValueTask DisposeAsync() { if (_serviceScope is IAsyncDisposable ad) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs index 148394fbb34219..0df7a1f83a5665 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs @@ -9,10 +9,20 @@ namespace Microsoft.Extensions.DependencyInjection /// Defines a disposable service scope. /// /// - /// The method ends the scope lifetime. Once Dispose + /// + /// The method ends the scope lifetime. Once /// is called, any scoped services and any transient services that have been resolved from /// will be /// disposed. + /// + /// + /// If the scope implementation also implements , prefer calling + /// over . If + /// any resolved service implements but not , + /// calling will throw an + /// (or an if multiple such services are resolved). + /// Consider using to ensure is always called. + /// /// public interface IServiceScope : IDisposable { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index aa62e11a10be98..e66bb2ee0d6670 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -169,14 +169,32 @@ internal object GetRequiredKeyedService(Type serviceType, object? serviceKey, Se internal bool IsDisposed() => _disposed; - /// + /// + /// Disposes the service provider and all resolved services that implement . + /// + /// + /// Prefer calling over this method. If any resolved service implements + /// but not , this method throws an + /// (or an if multiple such + /// services are resolved). Use to properly handle all disposable services, + /// or explicitly perform sync-over-async on the caller side if synchronous disposal is required. + /// + /// A resolved service implements but not . + /// Multiple resolved services implement but not . public void Dispose() { DisposeCore(); Root.Dispose(); } - /// + /// + /// Asynchronously disposes the service provider and all resolved services that implement or . + /// + /// + /// This is the preferred disposal method. Unlike , this method handles services that implement + /// only without throwing. + /// + /// A value task that represents the asynchronous operation. public ValueTask DisposeAsync() { DisposeCore(); From 4c6d9e0c4375cdf3181b1a1c62adf61c23a745ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:51:05 +0000 Subject: [PATCH 3/4] docs: simplify DI Dispose/DisposeAsync docs per review feedback Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com> --- .../src/AsyncServiceScope.cs | 13 +++---------- .../src/IServiceScope.cs | 3 +-- .../src/ServiceProvider.cs | 12 +++--------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs index 82cded2d4883ac..38009064e83e05 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs @@ -36,12 +36,10 @@ public AsyncServiceScope(IServiceScope serviceScope) /// /// Prefer calling over this method. If any resolved service implements /// but not , this method throws an - /// (or an if multiple such - /// services are resolved). Use to properly handle all disposable services, - /// or explicitly perform sync-over-async on the caller side if synchronous disposal is required. + /// . Use to properly handle all + /// disposable services, or explicitly perform sync-over-async on the caller side if synchronous + /// disposal is required. /// - /// A resolved service implements but not . - /// Multiple resolved services implement but not . public void Dispose() { _serviceScope.Dispose(); @@ -50,11 +48,6 @@ public void Dispose() /// /// Asynchronously ends the scope lifetime and disposes all resolved services that implement or . /// - /// - /// This is the preferred disposal method. When the underlying scope implements , - /// this method handles services that implement only without throwing. - /// When it does not, this method falls back to calling . - /// /// A value task that represents the asynchronous operation. public ValueTask DisposeAsync() { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs index 0df7a1f83a5665..9ba59696f5fa33 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs @@ -19,8 +19,7 @@ namespace Microsoft.Extensions.DependencyInjection /// If the scope implementation also implements , prefer calling /// over . If /// any resolved service implements but not , - /// calling will throw an - /// (or an if multiple such services are resolved). + /// calling will throw an . /// Consider using to ensure is always called. /// /// diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index e66bb2ee0d6670..629b4f8c2542cb 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -175,12 +175,10 @@ internal object GetRequiredKeyedService(Type serviceType, object? serviceKey, Se /// /// Prefer calling over this method. If any resolved service implements /// but not , this method throws an - /// (or an if multiple such - /// services are resolved). Use to properly handle all disposable services, - /// or explicitly perform sync-over-async on the caller side if synchronous disposal is required. + /// . Use to properly handle all + /// disposable services, or explicitly perform sync-over-async on the caller side if synchronous + /// disposal is required. /// - /// A resolved service implements but not . - /// Multiple resolved services implement but not . public void Dispose() { DisposeCore(); @@ -190,10 +188,6 @@ public void Dispose() /// /// Asynchronously disposes the service provider and all resolved services that implement or . /// - /// - /// This is the preferred disposal method. Unlike , this method handles services that implement - /// only without throwing. - /// /// A value task that represents the asynchronous operation. public ValueTask DisposeAsync() { From e953d194c34425230e17ae960472065503866016 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:45:48 +0000 Subject: [PATCH 4/4] docs: revert DI docs changes to AsyncServiceScope and IServiceScope Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com> --- .../src/AsyncServiceScope.cs | 16 ++-------------- .../src/IServiceScope.cs | 11 +---------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs index 38009064e83e05..230ead2bd3eed9 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs @@ -30,25 +30,13 @@ public AsyncServiceScope(IServiceScope serviceScope) /// public IServiceProvider ServiceProvider => _serviceScope.ServiceProvider; - /// - /// Ends the scope lifetime and disposes all resolved services. - /// - /// - /// Prefer calling over this method. If any resolved service implements - /// but not , this method throws an - /// . Use to properly handle all - /// disposable services, or explicitly perform sync-over-async on the caller side if synchronous - /// disposal is required. - /// + /// public void Dispose() { _serviceScope.Dispose(); } - /// - /// Asynchronously ends the scope lifetime and disposes all resolved services that implement or . - /// - /// A value task that represents the asynchronous operation. + /// public ValueTask DisposeAsync() { if (_serviceScope is IAsyncDisposable ad) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs index 9ba59696f5fa33..148394fbb34219 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/IServiceScope.cs @@ -9,19 +9,10 @@ namespace Microsoft.Extensions.DependencyInjection /// Defines a disposable service scope. /// /// - /// - /// The method ends the scope lifetime. Once + /// The method ends the scope lifetime. Once Dispose /// is called, any scoped services and any transient services that have been resolved from /// will be /// disposed. - /// - /// - /// If the scope implementation also implements , prefer calling - /// over . If - /// any resolved service implements but not , - /// calling will throw an . - /// Consider using to ensure is always called. - /// /// public interface IServiceScope : IDisposable {