If at least one of these objects implements the IAsyncDisposable
interface, then the composition implements IAsyncDisposable
as well. To dispose of all created singleton instances in an asynchronous manner, simply dispose of the composition instance in an asynchronous manner:
interface IDependency
{
bool IsDisposed { get; }
}
class Dependency : IDependency, IAsyncDisposable
{
public bool IsDisposed { get; private set; }
public ValueTask DisposeAsync()
{
IsDisposed = true;
return ValueTask.CompletedTask;
}
}
interface IService
{
public IDependency Dependency { get; }
}
class Service(IDependency dependency) : IService
{
public IDependency Dependency { get; } = dependency;
}
DI.Setup(nameof(Composition))
// This hint indicates to not generate methods such as Resolve
.Hint(Hint.Resolve, "Off")
.Bind().As(Lifetime.Singleton).To<Dependency>()
.Bind().To<Service>()
.Root<IService>("Root");
IDependency dependency;
await using (var composition = new Composition())
{
var service = composition.Root;
dependency = service.Dependency;
}
dependency.IsDisposed.ShouldBeTrue();
The following partial class will be generated:
partial class Composition: IDisposable, IAsyncDisposable
{
private readonly Composition _root;
private readonly object _lock;
private object[] _disposables;
private int _disposeIndex;
private Dependency? _singletonDependency39;
[OrdinalAttribute(20)]
public Composition()
{
_root = this;
_lock = new object();
_disposables = new object[1];
}
internal Composition(Composition parentScope)
{
_root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root;
_lock = _root._lock;
_disposables = parentScope._disposables;
}
public IService Root
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_root._singletonDependency39 == null)
{
lock (_lock)
{
if (_root._singletonDependency39 == null)
{
_root._singletonDependency39 = new Dependency();
_root._disposables[_root._disposeIndex++] = _root._singletonDependency39;
}
}
}
return new Service(_root._singletonDependency39!);
}
}
public void Dispose()
{
int disposeIndex;
object[] disposables;
lock (_lock)
{
disposeIndex = _disposeIndex;
_disposeIndex = 0;
disposables = _disposables;
_disposables = new object[1];
_singletonDependency39 = null;
}
while (disposeIndex-- > 0)
{
switch (disposables[disposeIndex])
{
case IAsyncDisposable asyncDisposableInstance:
try
{
var valueTask = asyncDisposableInstance.DisposeAsync();
if (!valueTask.IsCompleted)
{
valueTask.AsTask().Wait();
}
}
catch (Exception exception)
{
OnDisposeAsyncException(asyncDisposableInstance, exception);
}
break;
}
}
}
partial void OnDisposeException<T>(T disposableInstance, Exception exception) where T : IDisposable;
public async ValueTask DisposeAsync()
{
int disposeIndex;
object[] disposables;
lock (_lock)
{
disposeIndex = _disposeIndex;
_disposeIndex = 0;
disposables = _disposables;
_disposables = new object[1];
_singletonDependency39 = null;
}
while (disposeIndex-- > 0)
{
switch (disposables[disposeIndex])
{
case IAsyncDisposable asyncDisposableInstance:
try
{
await asyncDisposableInstance.DisposeAsync();
}
catch (Exception exception)
{
OnDisposeAsyncException(asyncDisposableInstance, exception);
}
break;
}
}
}
partial void OnDisposeAsyncException<T>(T asyncDisposableInstance, Exception exception) where T : IAsyncDisposable;
}
Class diagram:
classDiagram
class Composition {
<<partial>>
+IService Root
}
Composition --|> IDisposable
Composition --|> IAsyncDisposable
Dependency --|> IDependency
Dependency --|> IAsyncDisposable
class Dependency {
+Dependency()
}
Service --|> IService
class Service {
+Service(IDependency dependency)
}
class IDependency {
<<interface>>
}
class IAsyncDisposable {
<<interface>>
}
class IService {
<<interface>>
}
Composition ..> Service : IService Root
Service o-- "Singleton" Dependency : IDependency