Skip to content

Commit 61200f3

Browse files
committed
Upgraded to Simple Injector v5.2. Fixes #7
1 parent 21b2ce2 commit 61200f3

File tree

11 files changed

+138
-47
lines changed

11 files changed

+138
-47
lines changed

src/SimpleInjector.Integration.AspNetCore.Mvc.Core/SimpleInjector.Integration.AspNetCore.Mvc.Core.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<Description>Integration library for ASP.NET Core MVC core features for Simple Injector. This includes controller integration.</Description>
44
<AssemblyTitle>Simple Injector ASP.NET Core MVC Core Integration</AssemblyTitle>
55
<NeutralLanguage>en-US</NeutralLanguage>
6-
<VersionPrefix>5.1.1</VersionPrefix>
7-
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.1.1</PackageReleaseNotes>
6+
<VersionPrefix>5.2.0</VersionPrefix>
7+
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.2.0</PackageReleaseNotes>
88
<AssemblyVersion>5.0.0.0</AssemblyVersion>
99
<Authors>Simple Injector Contributors</Authors>
1010
<TargetFrameworks>netstandard2.0</TargetFrameworks>
@@ -27,8 +27,8 @@
2727
</PropertyGroup>
2828

2929
<ItemGroup>
30-
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.0.0" />
31-
<PackageReference Include="SimpleInjector" Version="5.1.0" />
30+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.1.0" />
31+
<PackageReference Include="SimpleInjector" Version="5.2.0" />
3232
</ItemGroup>
3333

3434
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">

src/SimpleInjector.Integration.AspNetCore.Mvc.ViewFeatures/SimpleInjector.Integration.AspNetCore.Mvc.ViewFeatures.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<Description>Integration library for ASP.NET Core MVC view features for Simple Injector. This includes view component integration.</Description>
44
<AssemblyTitle>Simple Injector ASP.NET Core MVC View Features Integration</AssemblyTitle>
55
<NeutralLanguage>en-US</NeutralLanguage>
6-
<VersionPrefix>5.1.1</VersionPrefix>
7-
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.1.1</PackageReleaseNotes>
6+
<VersionPrefix>5.2.0</VersionPrefix>
7+
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.2.0</PackageReleaseNotes>
88
<AssemblyVersion>5.0.0.0</AssemblyVersion>
99
<Authors>Simple Injector Contributors</Authors>
1010
<TargetFrameworks>netstandard2.0</TargetFrameworks>
@@ -28,9 +28,9 @@
2828
</PropertyGroup>
2929

3030
<ItemGroup>
31-
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.0.0" />
32-
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="2.0.0" />
33-
<PackageReference Include="SimpleInjector" Version="5.1.0" />
31+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.1.0" />
32+
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="2.1.0" />
33+
<PackageReference Include="SimpleInjector" Version="5.2.0" />
3434
</ItemGroup>
3535

3636
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">

src/SimpleInjector.Integration.AspNetCore.Mvc/SimpleInjector.Integration.AspNetCore.Mvc.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<Description>Integration library for ASP.NET Core MVC for Simple Injector. This package adds tag helper and Razor Pages integration on top of the core functionality.</Description>
44
<AssemblyTitle>Simple Injector ASP.NET Core MVC Integration</AssemblyTitle>
55
<NeutralLanguage>en-US</NeutralLanguage>
6-
<VersionPrefix>5.1.1</VersionPrefix>
7-
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.1.1</PackageReleaseNotes>
6+
<VersionPrefix>5.2.0</VersionPrefix>
7+
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.2.0</PackageReleaseNotes>
88
<AssemblyVersion>5.0.0.0</AssemblyVersion>
99
<Authors>Simple Injector Contributors</Authors>
1010
<TargetFrameworks>netstandard2.0</TargetFrameworks>
@@ -29,9 +29,9 @@
2929
</PropertyGroup>
3030

3131
<ItemGroup>
32-
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.0.0" />
33-
<PackageReference Include="Microsoft.AspNetCore.Mvc.RazorPages" Version="2.0.0" />
34-
<PackageReference Include="SimpleInjector" Version="5.1.0" />
32+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.1.0" />
33+
<PackageReference Include="Microsoft.AspNetCore.Mvc.RazorPages" Version="2.1.0" />
34+
<PackageReference Include="SimpleInjector" Version="5.2.0" />
3535
</ItemGroup>
3636

3737
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">

src/SimpleInjector.Integration.AspNetCore/RequestScopingStartupFilter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,18 @@ private void ConfigureRequestScoping(IApplicationBuilder builder)
3434
{
3535
builder.Use(async (httpContext, next) =>
3636
{
37-
await using (var scope = AsyncScopedLifestyle.BeginScope(this.container))
37+
Scope scope = AsyncScopedLifestyle.BeginScope(this.container);
38+
39+
try
3840
{
3941
scope.SetItem(HttpContextKey, httpContext);
4042

4143
await next();
4244
}
45+
finally
46+
{
47+
await scope.DisposeScopeAsync();
48+
}
4349
});
4450
}
4551
}

src/SimpleInjector.Integration.AspNetCore/SimpleInjector.Integration.AspNetCore.csproj

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<Description>Integration library for ASP.NET Core for Simple Injector.</Description>
44
<AssemblyTitle>Simple Injector ASP.NET Core Integration</AssemblyTitle>
55
<NeutralLanguage>en-US</NeutralLanguage>
6-
<VersionPrefix>5.1.1</VersionPrefix>
7-
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.1.1</PackageReleaseNotes>
6+
<VersionPrefix>5.2.0</VersionPrefix>
7+
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.2.0</PackageReleaseNotes>
88
<AssemblyVersion>5.0.0.0</AssemblyVersion>
99
<Authors>Simple Injector Contributors</Authors>
1010
<TargetFrameworks>netstandard2.0</TargetFrameworks>
@@ -29,11 +29,10 @@
2929
</PropertyGroup>
3030

3131
<ItemGroup>
32-
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.0.0" />
33-
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.0" />
34-
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.0.0" />
35-
<PackageReference Include="SimpleInjector" Version="5.1.0" />
36-
<PackageReference Include="SimpleInjector.Integration.ServiceCollection" Version="5.0.2" />
32+
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.1.0" />
33+
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.0" />
34+
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.1.0" />
35+
<PackageReference Include="SimpleInjector" Version="5.2.0" />
3736
</ItemGroup>
3837

3938
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
@@ -44,7 +43,12 @@
4443
<ItemGroup>
4544
<None Include="..\Graphics\simpleinjector.png" Pack="true" PackagePath="" />
4645
</ItemGroup>
47-
46+
47+
<ItemGroup>
48+
<ProjectReference Include="..\SimpleInjector.Integration.ServiceCollection\SimpleInjector.Integration.ServiceCollection.csproj" />
49+
<ProjectReference Include="..\SimpleInjector.Integration.GenericHost\SimpleInjector.Integration.GenericHost.csproj" />
50+
</ItemGroup>
51+
4852
<Target Name="PostcompileScript" AfterTargets="Build" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
4953
<Exec Command="dotnet pack --no-build --configuration $(Configuration)" />
5054
</Target>

src/SimpleInjector.Integration.AspNetCore/SimpleInjectorAddOptionsAspNetCoreExtensions.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ public static SimpleInjectorAspNetCoreBuilder AddAspNetCore(
7575

7676
services.UseSimpleInjectorAspNetRequestScoping(container);
7777

78+
if (options.DisposeContainerWithServiceProvider)
79+
{
80+
AddContainerDisposalOnShutdown(options, services);
81+
}
82+
7883
return new SimpleInjectorAspNetCoreBuilder(options);
7984
}
8085

@@ -107,5 +112,44 @@ private static IServiceProviderAccessor CreateServiceProviderAccessor(
107112
return options.ServiceProviderAccessor;
108113
}
109114
}
115+
116+
private static void AddContainerDisposalOnShutdown(
117+
SimpleInjectorAddOptions options, IServiceCollection services)
118+
{
119+
// DisposeContainerWithServiceProvider only support synchronous disposal, so we replace this with an
120+
// ASP.NET Core-specific implementation that actually supports asynchronous disposal. This can be done
121+
// with an IHostedService implementation.
122+
services.AddSingleton<ContainerDisposeWrapper>();
123+
124+
options.Container.Options.ContainerLocking += (_, __) =>
125+
{
126+
// If there's no IServiceProvider, the property will throw, which is something we want to do at this
127+
// point, not later on, when an unregistered type is resolved.
128+
IServiceProvider provider = options.ApplicationServices;
129+
130+
// In order for the wrapper to get disposed of, it needs to be resolved once.
131+
provider.GetRequiredService<ContainerDisposeWrapper>();
132+
};
133+
134+
// By setting the property to false, we prevent the AddSimpleInjector method from adding its own shutdown
135+
// behavior.
136+
options.DisposeContainerWithServiceProvider = false;
137+
}
138+
139+
private sealed class ContainerDisposeWrapper : IDisposable
140+
{
141+
private readonly Container container;
142+
143+
public ContainerDisposeWrapper(Container container) => this.container = container;
144+
145+
public void Dispose()
146+
{
147+
// Since we're running in the context of ASP.NET Core, a call to GetResult() will not result in a dead-
148+
// lock. It isn't pretty, but it doesn't hurt either, as this is called just once at shutdown. As a
149+
// matter of fact, Microsoft's Microsoft.Extensions.Hosting.Internal.Host class takes the exact same
150+
// approach.
151+
this.container.DisposeContainerAsync().GetAwaiter().GetResult();
152+
}
153+
}
110154
}
111155
}

src/SimpleInjector.Integration.GenericHost/SimpleInjector.Integration.GenericHost.csproj

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>
44
Integrates Simple Injector with applications built upon .NET Generic Host.
55
</Description>
66
<AssemblyTitle>Simple Injector Generic Host Integration</AssemblyTitle>
77
<NeutralLanguage>en-US</NeutralLanguage>
8-
<VersionPrefix>5.0.0</VersionPrefix>
9-
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/5.0.0</PackageReleaseNotes>
8+
<VersionPrefix>5.2.0</VersionPrefix>
9+
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.2.0</PackageReleaseNotes>
1010
<AssemblyVersion>5.0.0.0</AssemblyVersion>
1111
<Authors>Simple Injector Contributors</Authors>
1212
<TargetFrameworks>netstandard2.0</TargetFrameworks>
@@ -34,14 +34,17 @@
3434
<ItemGroup>
3535
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.0" />
3636
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.0" />
37-
<PackageReference Include="SimpleInjector" Version="5.0.0" />
38-
<PackageReference Include="SimpleInjector.Integration.ServiceCollection" Version="5.0.0" />
37+
<PackageReference Include="SimpleInjector" Version="5.2.0" />
3938
</ItemGroup>
4039

4140
<ItemGroup>
4241
<None Include="..\Graphics\simpleinjector.png" Pack="true" PackagePath="" />
4342
</ItemGroup>
44-
43+
44+
<ItemGroup>
45+
<ProjectReference Include="..\SimpleInjector.Integration.ServiceCollection\SimpleInjector.Integration.ServiceCollection.csproj" />
46+
</ItemGroup>
47+
4548
<Target Name="PostcompileScript" AfterTargets="Build" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
4649
<Exec Command="dotnet pack --no-build --configuration $(Configuration)" />
4750
</Target>

src/SimpleInjector.Integration.GenericHost/SimpleInjectorGenericHostExtensions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,16 @@ public static SimpleInjectorAddOptions AddHostedService<THostedService>(
2929
throw new ArgumentNullException(nameof(options));
3030
}
3131

32+
var services = options.Services;
33+
3234
var registration = Lifestyle.Singleton.CreateRegistration<THostedService>(options.Container);
3335

3436
// Let the built-in configuration system dispose this instance.
3537
registration.SuppressDisposal = true;
3638

3739
options.Container.AddRegistration<THostedService>(registration);
3840

39-
options.Services.AddSingleton<IHostedService>(_ =>
41+
services.AddSingleton<IHostedService>(_ =>
4042
{
4143
return options.Container.GetInstance<THostedService>();
4244
});

src/SimpleInjector.Integration.ServiceCollection/SimpleInjector.Integration.ServiceCollection.csproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Description>
44
Integrates Simple Injector with applications that require the use of IServiceCollection for registration of framework components.
55
</Description>
66
<AssemblyTitle>Simple Injector IServiceCollection Integration</AssemblyTitle>
77
<NeutralLanguage>en-US</NeutralLanguage>
8-
<VersionPrefix>5.0.2</VersionPrefix>
9-
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/5.0.0</PackageReleaseNotes>
8+
<VersionPrefix>5.2.0</VersionPrefix>
9+
<PackageReleaseNotes>https://github.com/simpleinjector/SimpleInjector.Integration.AspNetCore/releases/tag/v5.2.0</PackageReleaseNotes>
1010
<AssemblyVersion>5.0.0.0</AssemblyVersion>
1111
<Authors>Simple Injector Contributors</Authors>
1212
<TargetFrameworks>netstandard2.0</TargetFrameworks>
@@ -32,11 +32,11 @@
3232
</PropertyGroup>
3333

3434
<ItemGroup>
35-
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" />
36-
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.0" />
37-
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="2.0.0" />
38-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
39-
<PackageReference Include="SimpleInjector" Version="5.0.0" />
35+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.0" />
36+
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.0" />
37+
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="2.1.0" />
38+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.0" />
39+
<PackageReference Include="SimpleInjector" Version="5.2.0" />
4040
</ItemGroup>
4141

4242
<ItemGroup>

src/SimpleInjector.Integration.ServiceCollection/SimpleInjectorAddOptions.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,13 @@ public IServiceProviderAccessor ServiceProviderAccessor
6868
public bool AutoCrossWireFrameworkComponents { get; set; } = true;
6969

7070
/// <summary>
71-
/// Gets or sets the value indicating whether the <see cref="Container"/> instance ued by the
71+
/// Gets or sets the value indicating whether the <see cref="Container"/> instance used by the
7272
/// application should be disposed when the framework's <see cref="IServiceProvider"/> is disposed.
7373
/// The <see cref="IServiceProvider"/> is typically disposed of on application shutdown, which is also
74-
/// the time to dispose the Container. The default value is <b>true</b>.
74+
/// the time to dispose the Container. The default value is <b>true</b>. Please disable this option in case
75+
/// asynchronous disposal is required. If there are Singleton registrations that implement IAsyncDisposable,
76+
/// this property should be set to false and the Container should be disposed of manually by calling
77+
/// <see cref="Container.DisposeContainerAsync"/>.
7578
/// </summary>
7679
/// <value>A boolean value.</value>
7780
public bool DisposeContainerWithServiceProvider { get; set; } = true;
@@ -83,7 +86,7 @@ public IServiceProviderAccessor ServiceProviderAccessor
8386
/// is called, or when ASP.NET Core resolves its hosted services (whatever comes first).
8487
/// </summary>
8588
/// <value>The <see cref="IServiceProvider"/> instance.</value>
86-
internal IServiceProvider ApplicationServices
89+
public IServiceProvider ApplicationServices
8790
{
8891
get
8992
{

src/SimpleInjector.Integration.ServiceCollection/SimpleInjectorServiceCollectionExtensions.cs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,6 @@ private static void AddAutoCrossWiring(SimpleInjectorAddOptions options)
438438
private static void AddContainerDisposalOnShutdown(
439439
IServiceCollection services, SimpleInjectorAddOptions options)
440440
{
441-
services.TryAddSingleton(options.Container);
442-
443441
// This wrapper implements disposable and allows the container to be disposed of when
444442
// IServiceProvider is disposed of. Just like Simple Injector, however, MS.DI will only
445443
// dispose of instances that are registered using this overload (not using AddSingleton<T>(T)).
@@ -452,7 +450,8 @@ private static void AddContainerDisposalOnShutdown(
452450
IServiceProvider provider = options.ApplicationServices;
453451

454452
// In order for the wrapper to get disposed of, it needs to be resolved once.
455-
provider.GetRequiredService<ContainerDisposeWrapper>();
453+
var wrapper = provider.GetRequiredService<ContainerDisposeWrapper>();
454+
wrapper.FrameworkProviderType = provider.GetType();
456455
};
457456
}
458457

@@ -648,15 +647,45 @@ private sealed class NullSimpleInjectorHostedService : IHostedService
648647
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
649648
}
650649

651-
private sealed class ContainerDisposeWrapper : IDisposable, IAsyncDisposable
650+
private sealed class ContainerDisposeWrapper : IDisposable
652651
{
653652
private readonly Container container;
654653

655654
public ContainerDisposeWrapper(Container container) => this.container = container;
656655

657-
public void Dispose() => this.container.Dispose();
656+
public Type FrameworkProviderType { get; internal set; } = typeof(IServiceProvider);
658657

659-
public ValueTask DisposeAsync() => this.container.DisposeAsync();
658+
public void Dispose()
659+
{
660+
try
661+
{
662+
// NOTE: We can't call container.DisposeContainerAsync().GetAwaiter().GetResult(), because we don't
663+
// know the context in which this library is running. If it's ASP.NET Core, it would be okay to
664+
// call GetResult(), but in case we're running in a UI framework, GetResult() might result in a
665+
// deadlock.
666+
this.container.Dispose();
667+
}
668+
catch (InvalidOperationException ex)
669+
when (ex.Message.Contains("IAsyncDisposable") && ex.Message.Contains("IDisposable"))
670+
{
671+
// When we get here, Dispose complained that there was a Singleton registration that implements
672+
// IAsyncDisposable, while this is a synchronous Dispose call.
673+
throw new InvalidOperationException(
674+
$"Simple Injector was configured to be disposed of together with the application's " +
675+
$"{this.FrameworkProviderType.ToFriendlyName()}. This configuration is not suited for " +
676+
$"asynchronous disposal. The Simple Injector Container, however, contains a Singleton that " +
677+
$"implements IAsyncDisposable, which cannot be disposed of synchronously. To fix this " +
678+
$"problem, configure Simple Injector by setting {nameof(SimpleInjectorAddOptions)}." +
679+
$"{nameof(SimpleInjectorAddOptions.DisposeContainerWithServiceProvider)} to false and " +
680+
$"manually call 'await Container.{nameof(Container.DisposeContainerAsync)}()' at " +
681+
$"application shutdown. e.g.:\n" +
682+
$"services.{nameof(SimpleInjectorServiceCollectionExtensions.AddSimpleInjector)}(container, " +
683+
$"options => {{ " +
684+
$"options.{nameof(SimpleInjectorAddOptions.DisposeContainerWithServiceProvider)} = false;" +
685+
$" }}). {ex.Message}",
686+
ex);
687+
}
688+
}
660689
}
661690
}
662691
}

0 commit comments

Comments
 (0)