I am having a service that uses the IAsyncDisposable
interface, because it holds an object that has the interface itself.
public class AddressImporter : IAddressImporter
{
private readonly IConsumer<Model> _consumer; // implementing IAsyncDisposable
}
I register the service like this:
services.AddTransient<IAddressImporter, AddressImporter>();
And inject it into my main service like this:
public MainService(IAddressImporter addressImporter) {
_addressImporter = addressImporter;
}
Will this leak the resource? If so how can I improve this, so DisposeAsync
is called on any exception in my main service?
I was thinking of adding IAsyncDisposable
to IAddressImporter
and calling IAddressImporter.DisposeAsync
on any exception or the end of my MainService
.
EDIT: When trying to use the service
public class AddressImporter : IAddressImporter
{
private readonly IConsumer<Model> _consumer; // implementing IAsyncDisposable
public async ValueTask DisposeAsync()
{
_logger.Information("Disposing {ResourceName}", nameof(PulsarAddressImporter));
await _consumer.DisposeAsync();
GC.SuppressFinalize(this);
}
}
I get the exception:
BackgroundService failed System.InvalidOperationException: 'AddressService.Web.Jobs.Test' type only implements IAsyncDisposable. Use DisposeAsync to dispose the container. System.InvalidOperationException: 'AddressService.Web.Jobs.Test' type only implements IAsyncDisposable. Use DisposeAsync to dispose the container. [15:23:56 INF] Application is shutting down...
Will this leak the resource?
It depends on lifecycle of the parent object and scope - services created by build-in DI container are disposed automatically when the scope is disposed:
class MyTransient : IAsyncDisposable
{
public bool Disposed { get; set; }
public ValueTask DisposeAsync()
{
Disposed = true;
return ValueTask.CompletedTask;
}
}
var services = new ServiceCollection();
services.AddTransient<MyTransient>();
var serviceProvider = services.BuildServiceProvider();
MyTransient myTransient;
await using (var scope = serviceProvider.CreateAsyncScope())
{
myTransient = scope.ServiceProvider.GetRequiredService<MyTransient>();
}
Console.WriteLine(myTransient.Disposed); // prints True
So unless the service is created from the root one it will disposed with everything else when the owner scope ends, so making AddressImporter
implementing IDisposable/IAsyncDisposable
should do the trick (if the exception leads to end of the owner scope).