Yes, the default life time for DbContext
is scoped. This is intended this way.
Instantiating DbContext
is pretty cheap and it makes sure that the your do not use to many resources. If you'd have a DbContext
with a singleton lifetime, then all records that you read once will be tracked by the DbContext
, unless you specifically disable tracking. This will require much more memory usage and it will keep growing.
And the more the DbContext
tracks, the lower the performance will be. That's why you often see DbContext
only used within a using(var context = new AppDbContext())
block.
In web applications however, using the using
block is bad, because the lifetime is managed by the framework and if you dispose it to early the calls after that will fail with an exception.
If you use transient lifetime on the other side, you will lose the "transaction" functionality. With scoped, the DbContext
has a transaction scope that's as long as the request.
If you need more fine-grained control, you have to use the Unit of Work pattern (which DbContext
already kind of utilize).
For your second question:
If you create a service, it must have a lifetime that's equal to the one of the scope or shorter (read: Scoped or transient).
If you explicitly need a longer life-time for a service, you should inject a DbContext
factory service or factory method into your service.
You can accomplish this with something like
services.AddTransient<Func<AppDbContext>>( (provider) => new Func<MyDbContext>( () => new AppDbContext()));
services.AddSingleton<IMySingletonService, MySingletonService>();
And your service may look like this:
public class MySingletonService : IMySingletonService, IDisposable
{
private readonly AppDbContext context;
public MySingletonService(Func<AppDbContext> contextFactory)
{
if(contextFactory == null)
throw new ArgumentNullException(nameof(contextFactory));
// it creates an transient factory, make sure to dispose it in `Dispose()` method.
// Since it's member of the MySingletonService, it's lifetime
// is effectively bound to it.
context = contextFactory();
}
}
You should pass IServiceScopeFactory
instance (it's singleton) into your task.
Inside task, when data arrives, you should create new CreateScope()
and request services from that scope. When data process finishes - dispose this scope (but hold reference to IServiceScopeFactory
for next run).
See this for example. I run small and fast tasks with this library.
For heavy / long running tasks, as Gert wrote, don't rely that your task will always run to completion. Be ready for restart, be ready for re-processing of the same data.
Best Answer
The lifetime is a parameter on
AddDbContext<>()
. See example:This will add it to the service collection with transient lifetime.