简体   繁体   中英

.NET Core DI allowing to inject scoped service into singleton on local machine

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?

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 . Eg 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.

Scope validation Docs

To be able to use scoped services within a singleton, you must create a scope manually. A new scope can be created by injecting an IServiceScopeFactory into your singleton service (the IServiceScopeFactory is itself a singleton, which is why this works). The IServiceScopeFactory has a CreateScope method, which is used for creating new scope instances.

public class MySingletonService
{
  private readonly IServiceScopeFactory _serviceScopeFactory;

  public MySingletonService(IServiceScopeFactory serviceScopeFactory)
  {
    _serviceScopeFactory = serviceScopeFactory;
  }

  public void Execute()
  {
    using (var scope = _serviceScopeFactory.CreateScope())
    {
      var myScopedService = scope.ServiceProvider.GetService<IMyScopedService>();    
      myScopedService.DoSomething();
    }
  }
}

The created scope has its own IServiceProvider , which you can access to resolve your scoped services.

It is important to make sure that the scope only exists for as long as is necessary, and that it is properly disposed of once you have finished with it. This is to avoid any issues of captive dependencies. Therefore, I would recommend:

  • Only define the scope within the method that you intend to use it. It might be tempting to assign it to a field for reuse elsewhere in the singleton service, but again this will lead to captive dependencies.
  • Wrap the scope in a using statement. This will ensure that the scope is properly disposed of once you have finished with it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM