I have a class that derives from BackgroundService
(IHostedService
) for running background tasks. This will be added to my services using builder.Services.AddHostedService<BackgroundTaskService>()
BackgroundService
's task runs for the entire duration of the web application, checking for queued data to process.
My question is, how do I instantiate an instance of DbContext
from this code?
I could have the BackgroundTaskService
constructor accept a DbContext
. But wouldn't that keep the DbContext
open forever?
And how else could I instantiate it without duplicating all the code to scan my settings file for the connection string, etc.?
CodePudding user response:
You can use scope service factory. Check here for reference.
Here you have an example:
// Injection
public class DataApi : BackgroundService
{
private readonly ILogger<DataApi> logger;
private readonly IServiceScopeFactory scopeFactory;
public DataApi(ILogger<DataApi> _logger, IConfiguration _cfg, IServiceScopeFactory _sSF)
{
logger = _logger;
scopeFactory = _sSF;
// e.g. data from appsettings.json
// var recovery = _cfg["Api:Recovery"];
}
// ...
// Usage
protected async Task DataCollector()
{
logger.LogInformation("Collector");
using (var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
var myList = await db.MyEntity
.AsNoTracking()
.Where(t => t.active)
.ToListAsync();
if (myList.Count == 0)
{
logger.LogInformation("Empty...");
return;
}
// logic...
}
await Task.CompletedTask;
}
CodePudding user response:
The recemmended approach is to inject IDbContextFactory<TContext>
as described in the following article:
Some application types (e.g. ASP.NET Core Blazor) use dependency injection but do not create a service scope that aligns with the desired DbContext lifetime. Even where such an alignment does exist, the application may need to perform multiple units-of-work within this scope. For example, multiple units-of-work within a single HTTP request.
In these cases, AddDbContextFactory can be used to register a factory for creation of DbContext instances.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(
options =>
options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"));
}
Then in your controller:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}