Permalink
Browse files

Resolved #30: Implemented IDisposable on AutofacServiceProvider.

  • Loading branch information...
Travis Illig
Travis Illig committed Aug 16, 2018
1 parent b24bf39 commit d7d85319560c03d0d04319e1276313c81bd829cf
@@ -33,19 +33,21 @@ namespace Autofac.Extensions.DependencyInjection
/// </summary>
/// <seealso cref="System.IServiceProvider" />
/// <seealso cref="Microsoft.Extensions.DependencyInjection.ISupportRequiredService" />
public class AutofacServiceProvider : IServiceProvider, ISupportRequiredService
public class AutofacServiceProvider : IServiceProvider, ISupportRequiredService, IDisposable
{
private readonly IComponentContext _componentContext;
private readonly ILifetimeScope _lifetimeScope;
private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="AutofacServiceProvider"/> class.
/// </summary>
/// <param name="componentContext">
/// The component context from which services should be resolved.
/// <param name="lifetimeScope">
/// The lifetime scope from which services will be resolved.
/// </param>
public AutofacServiceProvider(IComponentContext componentContext)
public AutofacServiceProvider(ILifetimeScope lifetimeScope)
{
this._componentContext = componentContext;
this._lifetimeScope = lifetimeScope;
}
/// <summary>
@@ -66,7 +68,7 @@ public AutofacServiceProvider(IComponentContext componentContext)
/// </exception>
public object GetRequiredService(Type serviceType)
{
return this._componentContext.Resolve(serviceType);
return this._lifetimeScope.Resolve(serviceType);
}
/// <summary>
@@ -81,7 +83,36 @@ public object GetRequiredService(Type serviceType)
/// </returns>
public object GetService(Type serviceType)
{
return this._componentContext.ResolveOptional(serviceType);
return this._lifetimeScope.ResolveOptional(serviceType);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
this._lifetimeScope.Dispose();
}
this._disposed = true;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
}
}
@@ -35,7 +35,7 @@ namespace Autofac.Extensions.DependencyInjection
internal class AutofacServiceScope : IServiceScope
{
private bool _disposed;
private readonly ILifetimeScope _lifetimeScope;
private readonly AutofacServiceProvider _serviceProvider;
/// <summary>
/// Initializes a new instance of the <see cref="AutofacServiceScope"/> class.
@@ -45,8 +45,7 @@ internal class AutofacServiceScope : IServiceScope
/// </param>
public AutofacServiceScope(ILifetimeScope lifetimeScope)
{
this._lifetimeScope = lifetimeScope;
this.ServiceProvider = this._lifetimeScope.Resolve<IServiceProvider>();
this._serviceProvider = new AutofacServiceProvider(lifetimeScope);
}
/// <summary>
@@ -55,7 +54,13 @@ public AutofacServiceScope(ILifetimeScope lifetimeScope)
/// <value>
/// An <see cref="IServiceProvider" /> that can be used to resolve dependencies from the scope.
/// </value>
public IServiceProvider ServiceProvider { get; }
public IServiceProvider ServiceProvider
{
get
{
return this._serviceProvider;
}
}
/// <summary>
/// Disposes of the lifetime scope and resolved disposable services.
@@ -74,17 +79,15 @@ public void Dispose()
/// </param>
protected virtual void Dispose(bool disposing)
{
if (this._disposed)
if (!this._disposed)
{
return;
}
if (disposing)
{
this._serviceProvider.Dispose();
}
if (disposing)
{
this._lifetimeScope.Dispose();
this._disposed = true;
}
this._disposed = true;
}
}
}
@@ -0,0 +1,116 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Autofac.Extensions.DependencyInjection.Test
{
/// <summary>
/// Additional tests to illustrate undocumented yet assumed behaviors in
/// the Microsoft.Extensions.DependencyInjection container/scope.
/// </summary>
public abstract class AssumedBehaviorTests
{
[Fact]
public void DisposingScopeAlsoDisposesServiceProvider()
{
// You can't resolve things from a scope's service provider
// if you dispose the scope.
var services = new ServiceCollection().AddScoped<DisposeTracker>();
var rootProvider = this.CreateServiceProvider(services);
var scope = rootProvider.CreateScope();
Assert.NotNull(scope.ServiceProvider.GetRequiredService<DisposeTracker>());
scope.Dispose();
Assert.Throws<ObjectDisposedException>(() => scope.ServiceProvider.GetRequiredService<DisposeTracker>());
}
[Fact]
public void DisposingScopeAndProviderOnlyDisposesObjectsOnce()
{
// Disposing the service provider and then the scope only
// runs one disposal on the resolved objects.
var services = new ServiceCollection().AddScoped<DisposeTracker>();
var rootProvider = this.CreateServiceProvider(services);
var scope = rootProvider.CreateScope();
var tracker = scope.ServiceProvider.GetRequiredService<DisposeTracker>();
((IDisposable)scope.ServiceProvider).Dispose();
Assert.True(tracker.Disposed);
Assert.Equal(1, tracker.DisposeCount);
scope.Dispose();
Assert.Equal(1, tracker.DisposeCount);
}
[Fact]
public void DisposingScopeServiceProviderStopsNewScopes()
{
// You can't create a new child scope if you've disposed of
// the parent scope service provider.
var rootProvider = this.CreateServiceProvider(new ServiceCollection());
var scope = rootProvider.CreateScope();
((IDisposable)scope.ServiceProvider).Dispose();
Assert.Throws<ObjectDisposedException>(() => scope.ServiceProvider.CreateScope());
}
[Fact]
public void DisposingScopeServiceProviderStopsScopeResolutions()
{
// You can't resolve things from a scope if you dispose the
// scope's service provider.
var services = new ServiceCollection().AddScoped<DisposeTracker>();
var rootProvider = this.CreateServiceProvider(services);
var scope = rootProvider.CreateScope();
Assert.NotNull(scope.ServiceProvider.GetRequiredService<DisposeTracker>());
((IDisposable)scope.ServiceProvider).Dispose();
Assert.Throws<ObjectDisposedException>(() => scope.ServiceProvider.GetRequiredService<DisposeTracker>());
}
[Fact]
public void ResolvedProviderNotSameAsParent()
{
// Resolving a provider from another provider yields a new object.
// (It's not just returning "this" - it's a different IServiceProvider.)
var parent = this.CreateServiceProvider(new ServiceCollection());
var resolved = parent.GetRequiredService<IServiceProvider>();
Assert.NotSame(parent, resolved);
}
[Fact]
public void ResolvedProviderUsesSameScopeAsParent()
{
// Resolving a provider from another provider will still resolve
// items from the same scope.
var services = new ServiceCollection().AddScoped<DisposeTracker>();
var root = this.CreateServiceProvider(services);
var scope = root.CreateScope();
var parent = scope.ServiceProvider;
var resolved = parent.GetRequiredService<IServiceProvider>();
Assert.Same(parent.GetRequiredService<DisposeTracker>(), resolved.GetRequiredService<DisposeTracker>());
}
[Fact]
public void ServiceProviderWillNotResolveAfterDispose()
{
// You can't resolve things from a service provider
// if you dispose it.
var services = new ServiceCollection().AddScoped<DisposeTracker>();
var rootProvider = this.CreateServiceProvider(services);
Assert.NotNull(rootProvider.GetRequiredService<DisposeTracker>());
((IDisposable)rootProvider).Dispose();
Assert.Throws<ObjectDisposedException>(() => rootProvider.GetRequiredService<DisposeTracker>());
}
protected abstract IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection);
private class DisposeTracker : IDisposable
{
public int DisposeCount { get; set; }
public bool Disposed { get; set; }
public void Dispose()
{
this.Disposed = true;
this.DisposeCount++;
}
}
}
}
@@ -0,0 +1,18 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Autofac.Extensions.DependencyInjection.Test
{
public class AutofacAssumedBehaviorTests : AssumedBehaviorTests
{
protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
var builder = new ContainerBuilder();
builder.Populate(serviceCollection);
var container = builder.Build();
return container.Resolve<IServiceProvider>();
}
}
}
@@ -0,0 +1,13 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Autofac.Extensions.DependencyInjection.Test
{
public class MicrosoftAssumedBehaviorTests : AssumedBehaviorTests
{
protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
return serviceCollection.BuildServiceProvider();
}
}
}

0 comments on commit d7d8531

Please sign in to comment.