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! :)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With