0

I have jobs with Quartz library, in this DI I create dbcontext as follows.

   services.AddDbContext<X>(opt =>
        {
          opt.UseSqlServer(
            connectionString: hostContext.Configuration.GetConnectionString("x"),
            configure => configure.MigrationsAssembly("y.x.y.z")
            );

        }, ServiceLifetime.Transient);

There are asynchronous processes in the jobs, when I tracked the ram usage, I found that the dbcontext object was not fully cleaned by the garbage collector and the ram usage increased a lot in 3-4 days. I can't use scope or singleton as DbContext lifetime.

What causes this memory leak in transient lifetime?

I will use the using block as a last resort

Is there any other solution to my problem?

Is it ok to force kill the object using dispose in the finaly block?

 finally
          {
            _logService.SystemLog(log);
            _context.Dispose();
          }
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • `I can't use scope or singleton as DbContext lifetime.` why? That's already covered in the docs (check eg using scoped services in BackgroundServices), and several duplicate questions. There are at least two mechanisms, create an explicit scope, or use a `DbContextFactory`. The one you need depends on your actual use case, but creating explicit scopes works always. And probably fixes other problems you may have with DI too – Panagiotis Kanavos Jun 05 '23 at 14:19
  • I think the dbcontext object does not die at the end of the transaction. – Zafer Kırık Jun 05 '23 at 14:22
  • The DbContext *is* the transaction - it's a Unit of Work, not a database transaction. You still haven't explained why you don't use it as a scoped service, but the duplicates show both ways to fix this – Panagiotis Kanavos Jun 05 '23 at 14:27
  • As for the `finally` block, disposing is the job of the `using` block. `finally` doesn't provide any advantage over `using`. As for "cleaning", unless you explicitly call `Dispose()`, cleanup will wait until the Garbage Collector runs, which may be only when there's memory pressure. That's not a .NET bug - why run an expensive process when there's no reason? – Panagiotis Kanavos Jun 05 '23 at 14:30
  • The .NET docs cover using scoped services in singleton or long-lived scenarios in [Use scoped services within a BackgroundService](https://learn.microsoft.com/en-us/dotnet/core/extensions/scoped-service?pivots=dotnet-6-0) and [Blazor Server with EF Core](https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-7.0). In Blazor Server and Desktop applciations, a "scope" is the entire user session so a separate scope is needed to handle Unit-of-Work scenarios – Panagiotis Kanavos Jun 05 '23 at 14:33
  • I think the source of the problem is the console application. It doesn't know when to collect the object in transient lifetime. Because it's not an http request!. I wrapped it with using blocks, no problem.thanks for your answers – Zafer Kırık Jun 20 '23 at 07:29
  • All .NET applications are console applications, including ASP.NET Core, workers and yes, desktop apps. All start as console then set up their host/infrastructure. A DbContext is meant to be *scoped*, not transient. ASP.NET Core applications define a scope for every request. When the scope gets disposed, all the objects created inside it are also disposed. Transient ones aren't. – Panagiotis Kanavos Jun 20 '23 at 07:33
  • Did you check the [Quartz.NET DI docs?](https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/microsoft-di-integration.html) Have you added the `Quartz.Extensions.DependencyInjection` package? As the docs say `As of Quartz.NET 3.3.2 all jobs produced by the default job factory are scoped jobs`. If the DbContext is registered as scoped (the default) and passed as a constructor parameter, it will be disposed when the job ends – Panagiotis Kanavos Jun 20 '23 at 07:39

0 Answers0