Skip to content

Commit 0f1d8e4

Browse files
author
Dustin Masters
committed
Add missing Dispose on StructureMapServiceProvider
This fixes a hang if disposable resources are registered through Structuremap
1 parent c38bb40 commit 0f1d8e4

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

src/StructureMap.Microsoft.DependencyInjection/StructureMapServiceProvider.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
namespace StructureMap
66
{
7-
public sealed class StructureMapServiceProvider : IServiceProvider, ISupportRequiredService
7+
public sealed class StructureMapServiceProvider : IServiceProvider, ISupportRequiredService, IDisposable
88
{
99
private readonly Stack<IContainer> _containers = new Stack<IContainer>();
10+
private bool _isDisposing = false;
1011

1112
public StructureMapServiceProvider(IContainer container)
1213
{
@@ -54,5 +55,20 @@ public void TeardownScope()
5455
child.Dispose();
5556
}
5657
}
58+
59+
public void Dispose()
60+
{
61+
if (_isDisposing)
62+
{
63+
return;
64+
}
65+
66+
_isDisposing = true;
67+
68+
foreach (var container in _containers)
69+
{
70+
container.Dispose();
71+
}
72+
}
5773
}
5874
}

test/StructureMap.Microsoft.DependencyInjection.Tests/StructureMapContainerTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class StructureMapContainerTests : DependencyInjectionSpecificationTests
1717
{
1818
"ResolvesMixedOpenClosedGenericsAsEnumerable",
1919
"DisposesInReverseOrderOfCreation",
20+
"DisposingScopeDisposesService"
2021
};
2122

2223
protected override IServiceProvider CreateServiceProvider(IServiceCollection services)
@@ -77,6 +78,52 @@ public void CanResolveIEnumerableWithDefaultConstructor()
7778
Assert.NotEmpty(logger.Factory.Providers);
7879
}
7980

81+
[Fact]
82+
public void StructureMap_DisposingScopeDisposesService()
83+
{
84+
// StructureMap does not seem to dispose Transient services unless they were created inside of a scope.
85+
// See also:
86+
// Lamar - https://github.com/JasperFx/lamar/blob/adc805705daae241ee1f8bfcd7a46f73530caa83/documentation/documentation/ioc/disposing.md#transients
87+
// Original tests here https://github.com/aspnet/DependencyInjection/blob/930037a4f8b74a9c1e30d881507a05bea0a7c2e0/src/DI.Specification.Tests/DependencyInjectionSpecificationTests.cs#L406
88+
89+
var collection = new ServiceCollection();
90+
collection.AddSingleton<IFakeSingletonService, FakeService>();
91+
collection.AddScoped<IFakeScopedService, FakeService>();
92+
collection.AddTransient<IFakeService, FakeService>();
93+
94+
var provider = CreateServiceProvider(collection);
95+
FakeService disposableService;
96+
FakeService transient1;
97+
FakeService transient2;
98+
FakeService singleton;
99+
100+
// Act and Assert
101+
using (var scope = provider.CreateScope())
102+
{
103+
disposableService = (FakeService)scope.ServiceProvider.GetService<IFakeScopedService>();
104+
transient1 = (FakeService)scope.ServiceProvider.GetService<IFakeService>();
105+
transient2 = (FakeService)scope.ServiceProvider.GetService<IFakeService>();
106+
singleton = (FakeService)scope.ServiceProvider.GetService<IFakeSingletonService>();
107+
108+
Assert.False(disposableService.Disposed);
109+
Assert.False(transient1.Disposed);
110+
Assert.False(transient2.Disposed);
111+
Assert.False(singleton.Disposed);
112+
}
113+
114+
Assert.True(disposableService.Disposed);
115+
Assert.True(transient1.Disposed);
116+
Assert.True(transient2.Disposed);
117+
Assert.False(singleton.Disposed);
118+
119+
var disposableProvider = provider as IDisposable;
120+
if (disposableProvider != null)
121+
{
122+
disposableProvider.Dispose();
123+
Assert.True(singleton.Disposed);
124+
}
125+
}
126+
80127
private interface ILoggerProvider { }
81128

82129
private class TestLoggerProvider : ILoggerProvider { }

0 commit comments

Comments
 (0)