3

Usually when using entity framework I'm using a "using" clause:

using (var db = new dbcontext())
{
    var blabla = db.table.tolist();
}

Now, we have migrated our platform into .Net Core (2) and since there is a great DI mechanism we have decided to use it. Now, instead of using the above way, now we are getting the dbcontext as a parameter in the constructor:

public MeaningfullNameController(dbcontext db)
{
    this.db = db;
}

And when another class needs to use the dbcontext we just pass the above dbcontext in the constructor of the second class.

public class NewClass(dbcontext db)
{
    this.db = db;
}

This situation got me thinking, how does the garbage collector handle this situation? Is there a difference from the first approach in terms of performance? memory leaks? etc.

Thanks, Shaul

Shaul Zuarets
  • 839
  • 2
  • 10
  • 20
  • 1
    See my answer [here](https://stackoverflow.com/a/40845639/455493). Basically it boils down that the ASP.NET Core Abstractions will dispose the scoped (per request) container at the end of the request and all disposable services that have been created during it (transient and scoped services, singletons survive it) – Tseng Nov 15 '17 at 17:41

1 Answers1

4

There is definitely a difference in terms of lifecycle.

In the first approach, you create the object (and allocate memory, and any other resources it needs) via new. The end of the using block will call Dispose on it, freeing any non-memory resources. When the object goes out of scope, the GC can see it is not being used and reclaim it at any time.

In the DI approach, the lifecycle is being managed by the DI-framework. The DI-framework will allocate it and Dispose it. The DI-framework may allocate it once for the entire lifetime of the application, once per invocation of the DI-supporting-method or anything in between. Thus, the lifetime is probably longer and might be substantially longer. If the object lives for a longer time, that means the .NET Core runtime system has allocate it less often and reclaim it less often. It does less work. But it means the objects resources stay used for longer: a connection to the database and any handles will be kept around longer. If you are not using the db only once, for example, during the lifetime of the application and you keep it active for the entire lifetime, you are just wasting resources that you dont need.

In other words, there isn't a simple answer. For resources that are being used frequently, using DI with a lifecycle of per-method-call compared to explicitly calling new + Dispose will involve similar amounts of work. For other types of uses, it will depend on what you are doing and what will "better" for your use case.

In general though, this isn't something you should try and optimize for, unless you know this is causing performance issues. Optimize for the developer first.

omajid
  • 14,165
  • 4
  • 47
  • 64
  • thanks for the great answer. Our flow is a web api, so each request life cycle is rather short (for my understanding) so as for my understanding the DI approach will be better here since the GC is "working less" and it doesn't matter the connection to the DB stays open since since the request life cycle is short. Did I understood it correctly? – Shaul Zuarets Nov 15 '17 at 17:44
  • Just to be clear, the DI approach depends on how you configure the service. [Your choices are here](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection#service-lifetimes-and-registration-options). This doesn't directly tie to your request life cycle (unless you say your want the DI framework to create an object for that time only). For the DI framework to do the least amount of work, use a singleton, which means the object persists for the entire lifetime of your server (not just your request). – omajid Nov 15 '17 at 18:05
  • 1
    @omajid: EF Core doesn't work well with singleton scope **in an ASP.NET Core application**, because the same instance will be used for multiple requests and EF Core (as did EF before) isn't thread-safe – Tseng Nov 15 '17 at 18:08
  • @Tseng: It seems scoped will be most appropriate here: Singletone - Will work for the entire life time of the app, as Tseng said it's not thread safe. Transient - Since I'm passing the dbcontext into other classes, it will create more instances resulting in poor performance. Scoped, will create for the entire request life time. Seems like the best option. Did I got it right? – Shaul Zuarets Nov 15 '17 at 18:18
  • 1
    @ShaulZuarets; Scoped is already the default lifetime when you use `AddDbContext` extension method. There is an overload of that method to change it, but its not necessary in most cases. When you use factory pattern to create context (i.e. cause tenant is only known at runtime) then you got to dispose it yourself (i.e. keep track of the DbContext instances inside the factory and make the factory disposable, and dispose all contexts when the factory gets disposed) – Tseng Nov 15 '17 at 18:20
  • @Tseng Currently I'm not using Factory for this. I just inject my dbcontext (using addDbContext). Should I do anything else here? – Shaul Zuarets Nov 15 '17 at 18:23
  • Probably you have a some class that derives from some IdentityDbContext. Set a breakpoint in the constructor and you will see that by default a context is created for each request. – yonexbat Nov 15 '17 at 18:34
  • I know that a context is created with each request. The question is, is that a good practice? or should I go back to the "using" clause? @Tseng – Shaul Zuarets Nov 15 '17 at 19:08
  • @ShaulZuarets: Scoped DI DbContext is fine, unless you need to pull huge amount of data which you need to track (hence you can't use `AsNoTracking()`, then it would be suggested to release it before the request ends. All other cases are fine. The golden rule: Don't do preemptive optimization unless required or you can proof (via benchmarks) that its magnitudes faster than the other code) – Tseng Nov 15 '17 at 19:12