47
votes

In ASP.NET Core / EntityFramework Core, the services.AddDbContext<> method will add the specified context as a scoped service. It's my understanding that that is the suggested lifetime management for the dbcontext by Microsoft.

However, there is much debate in our engineer department over this and many feel that the context needs to be disposed of ASAP. So, what is the best way to configure the dbcontext as Transient that still maintains the same Repository pattern typically used (i.e. injecting the context directly into the repository's constructor) as well as supporting flexible unit testing?

2
Well, one disadvantage of Transient DbContext is, that you lose the Unit of Work functionality, unless you implement it yourself. By default the DbContext is transient and hence valid for the duration of the request and all services (not only the controller) will receive the same instance of it. If something goes wrong, you can just roll it back / not issue an SaveChanges command. With transient you lose that, each service will have its own instance of DbContext. - Tseng
Also I don't think there is much value to it, since you inject the DbContext via constructor and you will have to ensure in your your services don't call it after a dispose. If you really need the DbContext for a very short life time, it would be better to create a factory like wrapper to return you the transient factory and leave the default DbContext to scoped - Tseng
The technique I'm observing is the developer is injecting dboptions into the controller and passing this to the various services. The respective service then instantiates a new context from within the service (with the provided options) and passes this around to the various methods within the service. From my point of view this is no different than making dbcontext transient, but perhaps I'm missing the beneficial nuances of this type of implementation. - Jake Shakesworth
Injecting the dboptions and pass it to the calling services manually beats the purpose of having an DI/IoC system in the first place. You can abstract all of this in an transient factory and inject the factory and then in your code call it like using(var context = transientDbContextFactory.Create()) { ... } for the cases where you need the transient context and keep it at scoped for rest of your application. - Tseng
@Tseng Your first comment there isn't quite right, you say "By default the DbContext is transient" when in actual fact it's scoped by default. You then go on to describe it as "and hence valid for the duration of the request" which is a description of what scoped does! So I think the first part was just an accident. Just putting it out there as I read your comment and it made me completely doubt my knowledge of scoped vs transient for a few mins 😂 - lee_mcmullen

2 Answers

93
votes

The lifetime is a parameter on AddDbContext<>(). See example:

services.AddDbContext<ApplicationDbContext>(options =>
         options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), 
         ServiceLifetime.Transient);

This will add it to the service collection with transient lifetime.

-1
votes

You can also create a repository class and interface which has idbcontext constructor parameter. Have all of your controller constructors use this interface in their constructor. This can be added with addtransient. This way, Microsoft still controls the dbcontext as it sees fit. Context will be managed by runtime and injected when creating repository instances when creating controllers.