This question already has answers here:
How to consume a Scoped service from a Singleton?
(2 answers)
Closed 4 years ago.
I'm creating an ASP.NET Core application. It is using Entity Framework Core for database access. I'm using services.AddDbContext in Startup.cs and the DB Context is injected into my controller as expected.
I am also have a background task using IHostedService that is added as a Singleton. I'd like to have an instance of my DBContext in my implementation of IHostedService. When I try to do this I get a run time error that my IHostedService cannot consume a scoped service (my DB Context).
The DB Context class takes a parameter of DbContextOptions options and passes the options to the base constructor (DbContext).
I need to create an instance of my DB Context in my implementation of IHostedService (a singleton object) but I can't seem to figure out how to correctly create a new instance of DbContextOptions from my IHostedService implementation.
For resolving a Scoped Service from a Singleton Service, you could create the scoped service from IServiceProvider.
Here is the demo code:
public class DbHostedService : IHostedService
{
private readonly ILogger _logger;
public DbHostedService(IServiceProvider services,
ILogger<DbHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is starting.");
DoWork();
return Task.CompletedTask;
}
private void DoWork()
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var context =
scope.ServiceProvider
.GetRequiredService<ApplicationDbContext>();
var user = context.Users.LastOrDefault();
_logger.LogInformation(user?.UserName);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is stopping.");
return Task.CompletedTask;
}
}
Reference: Consuming a scoped service in a background task
I think you need not IHostedService. Myeby your need this
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BlogContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
services.AddSession();
}
Related
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.?
The recemmended approach is to inject IDbContextFactory<TContext> as described in the following article: Using a DbContext factory (e.g. for Blazor)
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())
{
// ...
}
}
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;
}
I have a razor pages with some classes such as scheduled tasks that run in the background. I have a IUnitofWork for the databases and uses EF.
In my schedule class "WorkerService : BackgroundService" it does routine backups and other tasks.
How can I reference the Database because I dont have DI due to not implementing razor pages?
Usually this is how I do it using DI on razor code files:
private readonly IUnitOfWork _unitOfWork;
public IndexModel(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
I am new to this DI and been in the udemy and microsoft site daily. I think I have to create a IUnit of work and pass in the ApplicationDbContext maybe in an ovveride? But how to get the context without DI.
Program.cs
builder.Services.AddHostedService<WorkerService>(); //Uses cronos to execute DoWork() every hour
WorkerService.cs
private const string schedule = "*/5 * * * *"; // every 5 for testing
private readonly CronExpression _cron;
public WorkerService()
{
_cron = CronExpression.Parse(schedule);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var utcNow = DateTime.UtcNow;
var nextUtc = _cron.GetNextOccurrence(utcNow);
await Task.Delay(nextUtc.Value - utcNow, stoppingToken);
await DoBackupAsync();
}
}
private static Task DoBackupAsync()
{
DoWork d = new DoWork();
return Task.FromResult("Done");
}
RazorApp/Pages
This is where I need to save data
RazorApp/ScheduledTasks/DoWork.cs
RazorApp/ScheduledTasks/WorkerService.cs
Attempting to DI either the IUnitOfWork or ApplicationDbContext
Further trying different examples like: https://dotnetcorecentral.com/blog/background-tasks
Results in this error as well: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: WebRazor.ScheduledTasks.BackgroundPrinter': Cannot consume scoped service 'WebRazor.DataAccess.ApplicationDbContext' from singleton 'Microsoft.Extensions.Hosting.IHostedService
public BackgroundPrinter(ILogger<BackgroundPrinter> logger, IWorker worker, ApplicationDbContext dbContext)
{
this.logger = logger;
applicationDbContext = dbContext;
}
Is this where I need to get it from the settings directly or is there a slick way to grab the Db Context?
Ok, wow I was all over the place. I implemented this and it worked.
public BackgroundPrinter(ILogger<BackgroundPrinter> logger, IWorker worker, IServiceProvider serviceProvider)
{
this.logger = logger;
unitOfWork = serviceProvider.CreateScope().ServiceProvider.GetRequiredService<IUnitOfWork>();
}
On construct pass in the Iserviceprovider and access this way. DI to the DoWork and its writing to the database.
Still not sure this is the best way to solve this problem, but I think I have to refactor my unitofwork because every now and then I get an error on writing
"A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext" but this may be easy to solve.
The background tasks with hosted services doc describes how to consume a scoped service in a background task:
Inject IServiceProvider in hosted service's ctor
Use the IServiceProvider to create scope during the execution
Use the scope to resolve required services
Personally for "timed" services like provided WorkerService in most cases I found useful to create scope per every iteration (especially if it uses EF internally). Something along this lines:
private readonly IServiceProvider _serviceProvider;
public WorkerService(IServiceProvider serviceProvider)
{
...
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var utcNow = DateTime.UtcNow;
var nextUtc = _cron.GetNextOccurrence(utcNow);
await Task.Delay(nextUtc.Value - utcNow, stoppingToken);
await DoBackupAsync(stoppingToken);
}
}
private static async Task DoBackupAsync(CancellationToken stoppingToken)
{
using (var scope = _serviceProvider.CreateScope()) // do not forget to dispose the scope
{
var backupService = scope.ServiceProvider.GetRequiredService<IBackupService>();
await backupService.BackUp(stoppingToken);
}
}
Let's say I have my own class QueueListener<TService, TPayload> inherited from BackgroundService. It opens TCP connection and listens incoming messages. On each message I would like to initialize service of TService type and pass deserialized from JSON instance of TPayload to it. TService is going to be registered as Transient, so it means to be lightweight and stateless as a handler for payload have to be (in my current task). For this purpose I am going to inject IServiceProvider in constructor of my QueueListener and create a scope on each message it receives. Does it sounds like a plan or am I overengineering? I want to avoid TService is singleton as well.
Documentation says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But I am not completely sure what does it means. There is no way to inject scoped service in BackgroundService because it has Singleton lifetime. Do they warn me to stop doing things like I do?
UPD #1
I explain why I suppose to create scope on each message. The idea behind that is to prevent listener to be blocked by message processing and to provide other developers possibility to create their own handlers and do some stuff on received message. Other developers can create database connections for instance while processing and I want it to be closed and released when handling is done.
Register TService as scoped and create a new scope per message. Then resolve TService from created scope. Just read Consuming a scoped service in a background task
You can write it like this:
services.AddHostedService<MyBackgroundService>();
services.AddScoped<IScopedServicePerMessage, ScopedServicePerMessage>();
...
public class MyBackgroundService : BackgroundService
{
private readonly IServiceProvider _sp;
public MyBackgroundService(IServiceProvider sp)
{
_sp = sp;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
DoWork(stoppingToken);
return Task.CompletedTask;
}
private void DoWork(CancellationToken stoppingToken)
{
while(true)
{
var msg = GetNextMessage();
using (var scope = _sp.CreateScope())
{
var servicePerMessage = scope.ServiceProvider.GetRequiredService<IScopedServicePerMessage>();
servicePerMessage.Handle(msg);
}
}
}
...
}
Regarding this:
It's dangerous to resolve a scoped service from a singleton. It may
cause the service to have incorrect state when processing subsequent requests.
It's about the case when you inject scoped service (ef core dbcontext, for instance) directly into singleton. It's not your case.
The documentation is referring to injecting a scoped service into a singleton service. Since the injection happens at the construction of the singleton object, the scoped service would be provided at that time. This will effectively increase the lifetime of the scoped service to that of a singleton service. This is dangerous because a scoped service lifetime is often chosen explicitly to ensure that the object gets disposed quickly again.
The most common example would be a database context which owns a database connection; you want to make sure that you free up this database connection as soon as possible to free up the resources. But if you injected the context into a singleton service, it would never get disposed.
That however does not mean that there is no way to consume scoped services within a singleton service. This is done by having the singleton service create a service scope from which it can then retrieve singleton services. It’s important though that this service scope is supposed to be short-lived. So take the example from ASP.NET Core itself, where a service scope is created for every request, and do something similar. For example in your case, you could do it for every incoming message if that makes sense for your application.
To create a service scope, you should inject an IServiceScopeFactory; you can then create a scope with it like this:
public async Task Process(TPayload payload)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.GetService<TService>();
await service.Process(payload);
}
}
This pattern is strictly only necessary if you need to consume scoped services. You could resolve all other services directly without creating a scope. If you can reuse the same service instance to process all payloads, you could also inject the service as a singleton (same as registering it as transient but resolving it only once). If you need a fresh instance for every payload, then consider creating a scope even if it isn’t strictly necessary.
First, transient services are not scoped services. Transient services are usually externally owned by your code and are created each time they are resolved from the container. Container does not cache transient services.
TService is going to be registered as Transient ... For this purpose I am going to inject IServiceProvider in constructor of my QueueListener and create a scope on each message it receives.
You do not need a scope for resolving transient services. Even if you create a scope, the scope still does not manage / own transient services. That, for example, ending the lifetime of the scope does not end lifetime of transient services.
You could simply use the IServiceProvider injected in QueueListener to resolve TService. And each TService resolved should be already like what you want
lightweight and stateless as a handler for payload
With regards to
Documentation says:
What the document says might not be relevant now since you are not using scoped services. But in case you want to know the reason:
It's dangerous to resolve a scoped service from a singleton.
Singleton is a special kind of scope. Singleton services are created and cached within a "root" scope of the container, which is essentially the container itself.
If you resolve scoped service from singleton, the lifetime / scope where the service instance is resolved and cached is likely to be the "root" scope. This leads to a problem where the scoped service instance being cached inside the container, and shared across multiple client requests.
This is dangerous, because scoped services are supposed to be
Scoped lifetime services (AddScoped) are created once per client request (connection).
I don't wanted my singleton class depending on the IServiceProvider directly.
So I've used a custom factory to accomplish this goal.
May this code example help others:
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IScopedBar, ScopedBar>();
services.AddSingleton<IScopedServiceFactory<IScopedBar>, ScopedServiceFactory<IScopedBar>>(
(provider) => {
var scope = provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IScopedBar>();
return new ScopedServiceFactory<IScopedBar>(() => new ScopedService<IScopedBar>(scope, service));
});
services.AddSingleton<ISingletonFoo, SingletonFoo>();
}
// ...
}
public interface ISingletonFoo
{
void DoSomethingUsingScopedServices();
}
public class SingletonFoo : ISingletonFoo
{
private readonly IScopedServiceFactory<IScopedBar> _barFactory;
public SingletonFoo(IScopedServiceFactory<IScopedBar> barFactory)
{
_barFactory = barFactory;
}
public void DoSomethingUsingScopedServices()
{
using var scopedService = _barFactory.CreateService();
scopedService.Service.DoSomething();
}
}
public interface IScopedBar
{
void DoSomething();
}
public class ScopedBar : IScopedBar
{
public void DoSomething()
{
// Do something
}
}
public interface IScopedService<T> : IDisposable
{
T Service { get; }
}
public interface IScopedServiceFactory<T>
{
IScopedService<T> CreateService();
}
public class ScopedService<T> : IScopedService<T>
{
private readonly IDisposable _scope;
public ScopedService(IDisposable scope, T service)
{
_scope = scope;
Service = service;
}
public T Service { get; }
public void Dispose()
{
_scope.Dispose();
}
}
public class ScopedServiceFactory<T> : IScopedServiceFactory<T>
{
private readonly Func<IScopedService<T>> _serviceFactory;
public ScopedServiceFactory(Func<IScopedService<T>> serviceFactory)
{
_serviceFactory = serviceFactory;
}
public IScopedService<T> CreateService()
{
return _serviceFactory();
}
}
Here is my code to consume scoped service:
public interface IScopedResolver<T> where T: class
{
TResult Resolve<TResult>(Func<T, TResult> dataFactory);
Task<TResult> ResolveAsync<TResult>(Func<T, Task<TResult>> dataFactory);
}
Implement class:
public class ScopedResolver<T> : IScopeResolver<T> where T: class
{
private readonly IServiceProvider _provider;
public ScopedResolver(IServiceProvider provider)
{
_provider = provider;
}
public TResult Resolve<TResult>(Func<T, TResult> dataFactory)
{
using IServiceScope scope = _provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<T>();
return dataFactory(service);
}
public async Task<TResult> ResolveAsync<TResult>(Func<T, Task<TResult>> dataFactory)
{
using var scope = _provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<T>();
return await dataFactory(service);
}
}
Register at startup:
services.AddSingleton(typeof(IScopedResolver<>), typeof(ScopedResolver<>));
using ScopedResolve:
public class ServiceA
{
private readonly IScopedResolver<DbContext> _context;
public ServiceA(IScopedResolver<DbContext> context)
{
_context = context;
}
public async Task<List<ClassOne>> GetListAsync()
{
return await _context.ResolveAsync(async s => await s.Set<ClassOne>().ToListAsync());
}
}
I am creating a ASP.NET Core web application. I am using a Repository through a library project. I reference it in the web application project.
The repository interface is as below:
public interface IPushNotificationRepository
{
IQueryable<PushNotification> Notifications
{
get;
}
IQueryable<Client> Clients
{
get;
}
void Add(PushNotification notification);
void Add(Client client);
void AddRange(IList<PushNotification> notifications);
bool AddIfNotAlreadySent(PushNotification notification);
void UpdateDelivery(PushNotification notification);
bool CheckIfClientExists(string client);
Client FindClient(int? id);
void Update(Client client);
void Delete(Client client);
}
Within the repository I inject the db context
public class PushNotificationRepository : IPushNotificationRepository
{
private readonly PushNotificationsContext _context;
public PushNotificationRepository(PushNotificationsContext context)
{
_context = context;
}
}
The configure services of the start up class is as below:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IPushNotificationRepository, PushNotificationRepository>();
services.AddDbContextPool<PushNotificationsContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("PushNotificationsConnection")));
}
In the controller class I consume the repository:
public class ClientsController : Controller
{
//private readonly PushNotificationsContext _context;
private readonly IPushNotificationRepository _pushNotificationRepository;
public ClientsController(IPushNotificationRepository pushNotificationRepository)
{
_pushNotificationRepository = pushNotificationRepository;
}
}
The repository classes are in a separate library project which is referenced by the web application project. The error I receive is:
System.AggregateException: 'Some services are not able to be
constructed (Error while validating the service descriptor
'ServiceType:
Services.Messaging.Data.Abstract.IPushNotificationRepository Lifetime:
Singleton ImplementationType:
Services.Messaging.Data.PushNotificationRepository': Cannot consume
scoped service 'Services.Messaging.Data.PushNotificationsContext' from
singleton
'Services.Messaging.Data.Abstract.IPushNotificationRepository'.)'
Would really appreciate some advise on this
A singleton cannot reference a Scoped instance. The error message is clear.
Cannot consume scoped service
'Services.Messaging.Data.PushNotificationsContext' from singleton
PushNotificationsContext is considered as a scoped service. You should almost never consume scoped service or transient service from a singleton. You should also avoid consuming transient service from a scoped service. Consuming scoped services it's a good practice to inject what you need, it gets cleaned up after the request automatically.
Either
services.AddTransient < IPushNotificationRepository, PushNotificationRepository>();
or
services.AddScoped< IPushNotificationRepository, PushNotificationRepository>();
will work fine, but check your design. Maybe this is not the behaviour you are looking for.
services.AddDbContext<PushNotificationsContext>() registers the PushNotificationsContext as a service with ServiceLifetime.Scoped which means that your PushNotificationsContext is created per web request. It is disposed when request is completed.
You could inject IServiceScopeFactory which is singleton into your repository, then create a new scope using CreateScope() and request the PushNotificationsContext service from that scope
public class PushNotificationRepository : IPushNotificationRepository
{
IServiceScopeFactory _serviceScopeFactory;
public PushNotificationRepository(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Add(PushNotification notification);
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<PushNotificationsContext>();
//other logic
}
}
}
Refer to c# - DataContext disposed in ASP.NET Core scheduler
I am trying to inject dependency into my middleware constructor as follows
public class CreateCompanyMiddleware
{
private readonly RequestDelegate _next;
private readonly UserManager<ApplicationUser> _userManager;
public CreateCompanyMiddleware(RequestDelegate next
, UserManager<ApplicationUser> userManager
)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await _next.Invoke(context);
}
}
My Startup.cs file looks like
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("IdentityConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
...
app.UseMiddleware<CreateCompanyMiddleware>();
...
But I am getting this error
An error occurred while starting the application.
InvalidOperationException: Cannot resolve scoped service 'Microsoft.AspNetCore.Identity.UserManager`1[Common.Models.ApplicationUser]' from root provider.
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
UserManager<ApplicationUser> is (by default) registered as a scoped dependency, whereas your CreateCompanyMiddleware middleware is constructed at app startup (effectively making it a singleton). This is a fairly standard error saying that you can't take a scoped dependency into a singleton class.
The fix is simple in this case - you can inject the UserManager<ApplicationUser> into your Invoke method:
public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
{
await _next.Invoke(context);
}
This is documented in ASP.NET Core Middleware: Per-request middleware dependencies:
Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by DI:
Another way to do that is to create a middleware by IMiddleware interface and register it as a service
For example , the middleware
public class CreateCompanyMiddlewareByInterface : IMiddleware
{
private readonly UserManager<ApplicationUser> _userManager;
public CreateCompanyMiddlewareByInterface(UserManager<ApplicationUser> userManager )
{
this._userManager = userManager;
}
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
return next(context);
}
}
and service registeration :
services.AddScoped<CreateCompanyMiddlewareByInterface>();
So why it happens ?
The middlewares using IMiddleware are built by UseMiddlewareInterface(appBuilder, middlewareType type) :
private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType)
{
return app.Use(next =>
{
return async context =>
{
var middlewareFactory = (IMiddlewareFactory)context.RequestServices.GetService(typeof(IMiddlewareFactory));
if (middlewareFactory == null) { /* throw ... */ }
var middleware = middlewareFactory.Create(middlewareType);
if (middleware == null) { /* throw ... */ }
try{
await middleware.InvokeAsync(context, next);
}
finally{
middlewareFactory.Release(middleware);
}
};
});
}
here the codes inside the context=>{} are executed per-request . So every time there's an incoming request , the var middleware = middlewareFactory.Create(middlewareType); will be executed and then ask for a middleware of middlewareType ( which is already registered as a service ) from the ServiceProvider .
As for by-convention middlewares , there's no factory creating them .
Those instances are all created by ActivatorUtilities.CreateInstance() at startup time . And any Invoke method of by-convention middlewares , such as
Task Invoke(HttpContext context,UserManager<ApplicationUser> userManage, ILoggerFactory loggeryFactory , ... )
will be compiled into a function like below :
Task Invoke(Middleware instance, HttpContext httpContext, IServiceprovider provider)
{
var useManager /* = get service from service provider */ ;
var log = /* = get service from service provider */ ;
// ...
return instance.Invoke(httpContext,userManager,log, ...);
}
As you see , here the instance is created at startup time , and those services of Invoke method are requested per request .