Home > OS >  How to instantiate DbContext from IHostingService
How to instantiate DbContext from IHostingService

Time:01-21

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())
    {
        // ...
    }
}
  • Related