Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,32 @@ public AsyncServiceScope(IServiceScope serviceScope)
/// <inheritdoc />
public IServiceProvider ServiceProvider => _serviceScope.ServiceProvider;

/// <inheritdoc />
/// <summary>
/// Ends the scope lifetime and disposes all resolved services.
/// </summary>
/// <remarks>
/// Prefer calling <see cref="DisposeAsync"/> over this method. If any resolved service implements
/// <see cref="IAsyncDisposable"/> but not <see cref="IDisposable"/>, this method throws an
/// <see cref="InvalidOperationException"/> (or an <see cref="AggregateException"/> if multiple such
/// services are resolved). Use <see cref="DisposeAsync"/> to properly handle all disposable services,
/// or explicitly perform sync-over-async on the caller side if synchronous disposal is required.
/// </remarks>
/// <exception cref="InvalidOperationException">A resolved service implements <see cref="IAsyncDisposable"/> but not <see cref="IDisposable"/>.</exception>
/// <exception cref="AggregateException">Multiple resolved services implement <see cref="IAsyncDisposable"/> but not <see cref="IDisposable"/>.</exception>
public void Dispose()
{
_serviceScope.Dispose();
}

/// <inheritdoc />
/// <summary>
/// Asynchronously ends the scope lifetime and disposes all resolved services that implement <see cref="IDisposable"/> or <see cref="IAsyncDisposable"/>.
/// </summary>
/// <remarks>
/// This is the preferred disposal method. When the underlying scope implements <see cref="IAsyncDisposable"/>,
/// this method handles services that implement only <see cref="IAsyncDisposable"/> without throwing.
/// When it does not, this method falls back to calling <see cref="Dispose"/>.
/// </remarks>
/// <returns>A value task that represents the asynchronous operation.</returns>
public ValueTask DisposeAsync()
{
if (_serviceScope is IAsyncDisposable ad)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ namespace Microsoft.Extensions.DependencyInjection
/// Defines a disposable service scope.
/// </summary>
/// <remarks>
/// The <see cref="System.IDisposable.Dispose"/> method ends the scope lifetime. Once Dispose
/// <para>
/// The <see cref="System.IDisposable.Dispose"/> method ends the scope lifetime. Once <see cref="System.IDisposable.Dispose"/>
/// is called, any scoped services and any transient services that have been resolved from
/// <see cref="Microsoft.Extensions.DependencyInjection.IServiceScope.ServiceProvider"/> will be
/// disposed.
/// </para>
/// <para>
/// If the scope implementation also implements <see cref="System.IAsyncDisposable"/>, prefer calling
/// <see cref="System.IAsyncDisposable.DisposeAsync"/> over <see cref="System.IDisposable.Dispose"/>. If
/// any resolved service implements <see cref="System.IAsyncDisposable"/> but not <see cref="System.IDisposable"/>,
/// calling <see cref="System.IDisposable.Dispose"/> will throw an <see cref="System.InvalidOperationException"/>
/// (or an <see cref="System.AggregateException"/> if multiple such services are resolved).
/// Consider using <see cref="AsyncServiceScope"/> to ensure <see cref="System.IAsyncDisposable.DisposeAsync"/> is always called.
/// </para>
/// </remarks>
public interface IServiceScope : IDisposable
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,32 @@ internal object GetRequiredKeyedService(Type serviceType, object? serviceKey, Se

internal bool IsDisposed() => _disposed;

/// <inheritdoc />
/// <summary>
/// Disposes the service provider and all resolved services that implement <see cref="IDisposable"/>.
/// </summary>
/// <remarks>
/// Prefer calling <see cref="DisposeAsync"/> over this method. If any resolved service implements
/// <see cref="IAsyncDisposable"/> but not <see cref="IDisposable"/>, this method throws an
/// <see cref="InvalidOperationException"/> (or an <see cref="AggregateException"/> if multiple such
/// services are resolved). Use <see cref="DisposeAsync"/> to properly handle all disposable services,
/// or explicitly perform sync-over-async on the caller side if synchronous disposal is required.
/// </remarks>
/// <exception cref="InvalidOperationException">A resolved service implements <see cref="IAsyncDisposable"/> but not <see cref="IDisposable"/>.</exception>
/// <exception cref="AggregateException">Multiple resolved services implement <see cref="IAsyncDisposable"/> but not <see cref="IDisposable"/>.</exception>
public void Dispose()
{
DisposeCore();
Root.Dispose();
}

/// <inheritdoc/>
/// <summary>
/// Asynchronously disposes the service provider and all resolved services that implement <see cref="IDisposable"/> or <see cref="IAsyncDisposable"/>.
/// </summary>
/// <remarks>
/// This is the preferred disposal method. Unlike <see cref="Dispose"/>, this method handles services that implement
/// only <see cref="IAsyncDisposable"/> without throwing.
/// </remarks>
/// <returns>A value task that represents the asynchronous operation.</returns>
public ValueTask DisposeAsync()
{
DisposeCore();
Expand Down
Loading