Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP fix: dispose IServiceProvider in MicrosoftDependencyResolver #1170

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Splat.Microsoft.Extensions.DependencyInjection;
/// Microsoft DI implementation for <see cref="IDependencyResolver"/>.
/// </summary>
/// <seealso cref="IDependencyResolver" />
public class MicrosoftDependencyResolver : IDependencyResolver
public class MicrosoftDependencyResolver : IDependencyResolver, IAsyncDisposable
{
private const string ImmutableExceptionMessage = "This container has already been built and cannot be modified.";
private readonly object _syncLock = new();
Expand Down Expand Up @@ -50,6 +50,38 @@ protected virtual IServiceProvider? ServiceProvider
}
}

/// <summary>
/// Updates this instance with a collection of configured services.
/// </summary>
/// <param name="services">An instance of <see cref="IServiceCollection"/>.</param>
public void UpdateContainer(IServiceCollection services)
{
#if NETSTANDARD || NETFRAMEWORK
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
#else
ArgumentNullException.ThrowIfNull(services);
#endif

if (_isImmutable)
{
throw new InvalidOperationException(ImmutableExceptionMessage);
}

lock (_syncLock)
{
if (_serviceProvider is not null)
{
DisposeServiceProvider(_serviceProvider);
_serviceProvider = null;
}

_serviceCollection = services;
}
}

/// <summary>
/// Updates this instance with a configured service Provider.
/// </summary>
Expand All @@ -67,8 +99,15 @@ public void UpdateContainer(IServiceProvider serviceProvider)

lock (_syncLock)
{
_serviceCollection = null;
// can be null if constructor using IServiceCollection was used.
// and no fetch of a service was called.
if (_serviceProvider is not null)
{
DisposeServiceProvider(_serviceProvider);
}

_serviceProvider = serviceProvider;
_serviceCollection = null;
_isImmutable = true;
}
}
Expand Down Expand Up @@ -144,6 +183,7 @@ public virtual void Register(Func<object?> factory, Type? serviceType, string? c
}

// required so that it gets rebuilt if not injected externally.
DisposeServiceProvider(_serviceProvider);
_serviceProvider = null;
}
}
Expand Down Expand Up @@ -178,6 +218,7 @@ public virtual void UnregisterCurrent(Type? serviceType, string? contract = null
}

// required so that it gets rebuilt if not injected externally.
DisposeServiceProvider(_serviceProvider);
_serviceProvider = null;
}
}
Expand All @@ -203,6 +244,7 @@ public virtual void UnregisterAll(Type? serviceType, string? contract = null)
if (_serviceCollection is null)
{
// required so that it gets rebuilt if not injected externally.
DisposeServiceProvider(_serviceProvider);
_serviceProvider = null;
return;
}
Expand All @@ -220,6 +262,7 @@ public virtual void UnregisterAll(Type? serviceType, string? contract = null)
}

// required so that it gets rebuilt if not injected externally.
DisposeServiceProvider(_serviceProvider);
_serviceProvider = null;
}
}
Expand Down Expand Up @@ -249,6 +292,16 @@ public virtual bool HasRegistration(Type? serviceType, string? contract = null)
&& keyedServiceProvider.GetKeyedService(serviceType, contract) is not null;
}

/// <inheritdoc/>
public async ValueTask DisposeAsync()
{
if (_serviceProvider is IAsyncDisposable d)
{
await d.DisposeAsync();
GC.SuppressFinalize(this);
}
}

/// <inheritdoc />
public void Dispose()
{
Expand All @@ -262,6 +315,18 @@ public void Dispose()
/// <param name="disposing">Whether or not the instance is disposing.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
DisposeServiceProvider(_serviceProvider);
}
}

private static void DisposeServiceProvider(IServiceProvider? sp)
{
if (sp is IDisposable d)
{
d.Dispose();
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public static class SplatMicrosoftExtensions
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
public static void UseMicrosoftDependencyResolver(this IServiceCollection serviceCollection) =>
#pragma warning disable CA2000
// Will be disposed with the InternalLocator
Locator.SetLocator(new MicrosoftDependencyResolver(serviceCollection));
#pragma warning restore CA2000

/// <summary>
/// Initializes an instance of <see cref="MicrosoftDependencyResolver"/> that overrides the default <see cref="Locator"/>
Expand All @@ -37,7 +40,10 @@ public static void UseMicrosoftDependencyResolver(this IServiceProvider serviceP
}
else
{
#pragma warning disable CA2000
// Will be disposed with the InternalLocator
Locator.SetLocator(new MicrosoftDependencyResolver(serviceProvider));
#pragma warning restore CA2000
}
}
}