Skip to content

Commit d8e8436

Browse files
[release/7.0-rc1] Fix leak caused by not disposing the scoped parent service provider (#74362)
* Fix leaks caused by not disposing the parent scoped ServiceProvider * Address the feedback Co-authored-by: Tarek Mahmoud Sayed <[email protected]>
1 parent 2b9aacb commit d8e8436

File tree

3 files changed

+26
-4
lines changed

3 files changed

+26
-4
lines changed

src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,20 @@ static async ValueTask Await(int i, ValueTask vt, List<object> toDispose)
187187
// No further changes to _state.Disposables, are allowed.
188188
_disposed = true;
189189

190-
// ResolvedServices is never cleared for singletons because there might be a compilation running in background
191-
// trying to get a cached singleton service. If it doesn't find it
192-
// it will try to create a new one which will result in an ObjectDisposedException.
190+
}
193191

194-
return _disposables;
192+
if (IsRootScope && !RootProvider.IsDisposed())
193+
{
194+
// If this ServiceProviderEngineScope instance is a root scope, disposing this instance will need to dispose the RootProvider too.
195+
// Otherwise the RootProvider will never get disposed and will leak.
196+
// Note, if the RootProvider get disposed first, it will automatically dispose all attached ServiceProviderEngineScope objects.
197+
RootProvider.Dispose();
195198
}
199+
200+
// ResolvedServices is never cleared for singletons because there might be a compilation running in background
201+
// trying to get a cached singleton service. If it doesn't find it
202+
// it will try to create a new one which will result in an ObjectDisposedException.
203+
return _disposables;
196204
}
197205
}
198206
}

src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ internal ServiceProvider(ICollection<ServiceDescriptor> serviceDescriptors, Serv
8989
/// <returns>The service that was produced.</returns>
9090
public object? GetService(Type serviceType) => GetService(serviceType, Root);
9191

92+
internal bool IsDisposed() => _disposed;
93+
9294
/// <inheritdoc />
9395
public void Dispose()
9496
{

src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using Microsoft.Extensions.DependencyInjection.Specification.Fakes;
56
using Xunit;
67

@@ -17,5 +18,16 @@ public void DoubleDisposeWorks()
1718
serviceProviderEngineScope.Dispose();
1819
serviceProviderEngineScope.Dispose();
1920
}
21+
22+
[Fact]
23+
public void RootEngineScopeDisposeTest()
24+
{
25+
var services = new ServiceCollection();
26+
ServiceProvider sp = services.BuildServiceProvider();
27+
var s = sp.GetRequiredService<IServiceProvider>();
28+
((IDisposable)s).Dispose();
29+
30+
Assert.Throws<ObjectDisposedException>(() => sp.GetRequiredService<IServiceProvider>());
31+
}
2032
}
2133
}

0 commit comments

Comments
 (0)