Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding DBContext Service to program.cs in Worker Project

Fairly new to all this, so apologies if I am doing anything stupid.

I am trying to implement a worker project with communication to a local SQL Server Express database I have setup.

I am storing the connection string in my AppSettings.Json as shown below

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ConnectionStrings": {
    "DBConnection": "Server=localhost\\SQLEXPRESS;Database=TwitterTesting;Trusted_Connection=True;"
  }
}

I then have a DBContext.cs file which stores the structure of my database. At the moment I only have one table 'tweets'.

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WorkerService1.Entities;

namespace WorkerService1
{
    public class DataContext : DbContext
    {
        public DataContext(DbContextOptions options) : base(options)
        {
        }

        public DbSet<tweets> tweets { get; set; }
    }
}

Tweets.cs has the column structure only.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace WorkerService1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddDbContextPool<DataContext>(
                        options => options.UseSqlServer(hostContext.Configuration.GetConnectionString("DBConnection"))
                    );
                    services.AddHostedService<Worker>();
                });
    }
}

Above is my program.cs file where I am trying to add the implementation of the DBContext, but I am getting the following error upon compilation:

System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: WorkerService1.Worker': Cannot consume scoped service 'WorkerService1.DataContext' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.)

My understanding from looking online it is something to do with dependency injection, but I am really confused with what I need to do.

Any help would be greatly appreciated! :)

like image 656
Paul James Avatar asked May 18 '26 17:05

Paul James


1 Answers

The problem is that you're trying to create a Scoped service of your DbContext, but the class that's going to use it (Worker) is a Singleton class - note the following two lines.

services.AddDbContextPool<DataContext>( /*omitted*/ );
services.AddHostedService<Worker>();

While neither of these explicitly state how the service is being added to DI, AddHostedService<Worker> is registering Worker class as a singleton - only one instance of Worker will be created, ever. AddDbContextPool<DataContext> is registering DataContext as a scoped service - one that should be constructed every time a scope is created in which DataContext is asked for.

The problem is that if Worker asks for DataContext in the constructor, you'll get a single DataContext that will live for the duration of the the Worker - which is going to be forever, because Worker is a singleton.

To get a scoped DataContext in your Worker class, you'll need to get the service provider from DI and create a scope each time your Worker iterates in a main method - for example:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Options;
using System;
using System.Threading;
using System.Threading.Tasks;
private readonly ILogger<Worker> _logger;
private readonly IServiceProvider _serviceProvider;

public Worker (ILogger<Worker> logger, IServiceProvider serviceProvider)
{
    _logger = logger;
    _serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        _logger.Log("Creating scope to get a new DataContext.");
        // this will give us a scoped service
        using var scope = _serviceProvider.CreateScope();
        var services = scope.ServiceProvider;
        // if DataContext were configured as singleton through AddSingleton<DataContext> 
        // in ConfigureServices, this would always be the same instance. Since it's added 
        // as Scoped, we'll get the same instance every time we ask for the service from 
        // our scope but each time we create a new scope it'll be a new instance.
        var context = services.GetService<DataContext>();
        // do something with context
        // we can validate that within our scope it's always the same object reference:
        _logger.Log("Get scoped service multiple times yields the same reference: {0}", object.ReferenceEquals(context, services.GetService<DataContext>());
        context.Dispose();
    }
}

For additional information, check out the following page - particularly paying heed to mentions of "Scoped", "Transient", and "Singleton". https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0

like image 79
Stroniax Avatar answered May 21 '26 06:05

Stroniax



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!