Reputation: 34169
I have the following code in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// EF
services.AddDbContext<MyDatabaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// domain services
services.AddScoped<ILookupService, LookupService>();
services.AddMemoryCache();
// singleton
services.AddSingleton<CacheManager>();
}
public class CacheManager
{
private readonly ILookupService _lookupService;
private readonly IMemoryCache _cache;
public CacheManager(ILookupService lookupService, IMemoryCache cache)
{
_lookupService = lookupService;
_cache = cache;
}
}
When I deploy the application on the dev server and try to access. I see exception
AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: UI.Helpers.CacheManager Lifetime: Singleton ImplementationType: UI.Helpers.CacheManager': Cannot consume scoped service 'ApplicationCore.Services.ILookupService' from singleton 'UI.Helpers.CacheManager'.)
Note that I understand why this is happening and I will fix it. Basically you cannot inject service with smaller scope into service with larger scope. The DI of ASP.NET Core trying to make sure you don’t do that.
What I am not understanding why I am not seeing this issue when I run application locally in VS?
Upvotes: 9
Views: 3277
Reputation: 493
MSDN has a solution to solve this problem.
Use scoped services within a BackgroundService
So, Just using (IServiceScope scope = _serviceProvider.CreateScope())
in your singleton server(CacheManager
) .
Your CacheManager
will be this:
public class CacheManager
{
private readonly IMemoryCache _cache;
private readonly IServiceProvider _serviceProvider;
public CacheManager( IMemoryCache cache
, IServiceProvider serviceProvider)
{
_cache = cache;
_serviceProvider = serviceProvider;
}
public void Execute()
{
using (IServiceScope scope = _serviceProvider.CreateScope())
{
var _lookupService =
scope.ServiceProvider.GetRequiredService<ILookupService>();
//... do what you want
}
}
}
Upvotes: 0
Reputation: 1553
I encountered this today while shooting myself in the foot.
Per the docs, the implementation of WebHost.CreateDefaultBuilder sets ServiceProviderOptions.ValidateScopes to true
only if the environment is Development
. E.g. By default, if any of the following environment variables are set to Development
: ASPNETCORE_ENVIRONMENT
, DOTNET_ENVIRONMENT
. I'm also getting the same behavior with Host.CreateDefaultBuilder.
With ServiceProviderOptions.ValidateScopes
set to false
, injecting a scoped service into a singleton one is not giving me a runtime error, but instead the dependency is resolved with some form of transient behavior.
I can only imagine that this setting exists to avoid the overhead of performing these validations on higher environments. Doing this at development time seems enough to me.
Just to reiterate what was already stated by @LP13. It's invalid to inject a scoped service into a singleton one. For that, you will have to create a scope inside the singleton instance.
I'm referencing .NET Core 3.1 docs but this is still true as of .NET 6.
Upvotes: 4