2

I have troubleshooted a memory leak within my application down to an improper use of a transient Idisposable service.

The service is implemented as

public interface IUnitOfWork : IDisposable, IAsyncDisposable
{

}

It is injected as

public static IServiceCollection AddUnitOfWork(this IServiceCollection services)
    => services.AddTransient<IUnitOfWork>(services => new UnitOfWork(services.GetRequiredService<IConfiguration>()));

and it is usually consumed like this

void DoStuff()
{
    using var uow = serviceProvider.GetRequiredService<IUnitOfWork>();
}

The thing is, there are way too many consumers that get the service that way, making it almost impossible to refactor the whole codebase.

Microsoft guidance states that a factory pattern should be used instead. Is there any way where I can change the injection part of the code and avoid the memory leak without changing every consumers?

I have tried using multiple ways of injecting the service, they all seem to cause a memory leak.

  • Is `serviceProvider` a root scope of the application? – Guru Stron Mar 01 '23 at 10:01
  • Sadly yes and I don't think there's a way around that with the current architecture – Niccolò Pecora Mar 01 '23 at 10:04
  • Maybe you can change the implementor of IUnitOfWork that it creates/destroys the dependency that it needs to be disposed on a per method call basis? That would make IDisposable needless and gets rid of the problem without a mayor change and just moves the disposable stuff into the methods of the IUnitOfWork implementor. – Ralf Mar 01 '23 at 11:45
  • Have you tried just registering it as `.AddTransient()` – itsdaniel0 Mar 01 '23 at 16:48
  • Yes, In any case the service provider keeps track of those instances even if they are disposed. – Niccolò Pecora Mar 02 '23 at 11:18

1 Answers1

1

It seems that the problem here is that you are working with the same scope (highly likely the root one) which will not release the objects (note that scope on the disposal will dispose the services which implement IDisposable). You need to rework your app so the UoW holding scope is disposed:

void DoStuff()
{
    using var serviceScope = serviceProvider.CreateScope();
    var uow = serviceScope.GetRequiredService<IUnitOfWork>(); // no need for using
} // uow will be disposed here with the holding scope

Sometime ago I've used this approach to capture such scenarios.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • This is clear to me, however the point of my question is to find a solution that does not affect the consumer code and only affects the injection part of the code – Niccolò Pecora Mar 01 '23 at 10:17
  • @NiccolòPecora AFAIK there is none without any code modifications on the caller side. Possibly something can be done by switching to another DI container which will not store the objects. Or some ugly reflection. – Guru Stron Mar 01 '23 at 10:23