ChatGPT解决这个技术问题 Extra ChatGPT

Configuring Dbcontext as Transient

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?

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.
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
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.
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 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 😂

j
juunas

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.


Will this work if the controller/service uses a Scoped lifetime? How would you request this DbContext when Microsoft.Extensions.DependencyInjection only works with constructor injection? Or am I missing something?
Sorry for the late reply. As a rule of thumb, you should not be using services with a smaller lifetime within another service. In those cases, your service can accept the transient service as a method parameter. Controllers should be transient though, only one is instantiated per request anyway :)
Be aware of a side effect when using dotnet core builtin container - it will track all created instances of DbContext causing a memory leak: github.com/aspnet/DependencyInjection/issues/456
If you acquire the instance from the scope created for the request, they will be disposed of at the end of the request when the scope is disposed though.
As @IvanSamygin wrote, please be aware that Microsoft's DI container keeps references to all disposable services, so that they can be disposed at the end of the request. However, if hosting the service in a Windows service, then the root scope lives as long as the service lives. So the safest approach is to always use Scoped for the DbContexts, or make sure that they are resolved as something that's not disposable (wrapped in Func<T>), or always create a new child scope everywhere you're resolving the DbContext. So the first rule of thumb is usually the simplest: just don't use transient.
D
Dharman

In my opinion, a good use case for registering a DbContext as a transient dependency is within worker services that are registered as singletons. You can not use scoped dependencies within singleton dependencies. So the only option you have is to register the DbContext as either singleton or transient. Something to bear in mind is that the injected DbContextOptions class lifetime also needs to be updated. You can do both by specifying the service lifetime as follows.

services.AddDbContext<DataContext>(options =>
        {
            options.UseMySQL(configurationRoot.GetConnectionString("DefaultConnection"));
            options.UseLazyLoadingProxies();
        }, ServiceLifetime.Transient, ServiceLifetime.Transient);

The third parameter is for the service lifetime of the DbContextOptions instance.


d
douglas.kirschman

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.


This is very vague without a code example. And the question is explicitly not to let Microsoft still control the dbcontext as it sees fit.