How to Use Scoped Service from Singelton Service in .NET Core

Below is a very common exception in .net core, and we get this exception when we use the scope service inside the singleton service.

We should be very careful about the lifetime of objects in the .net core.

Suppose we created Two services, one registered as Singleton and the other as Scoped. Consider the below code.

public interface IScopedService
{  
}

public interface ISingletonService
{
}

/// Scoped service
public class ScopedService : IScopedService
{
    public ScopedService()
    {
    }
}

/// Singleton Service, here we are injecting scoped service object
public class SingletonService : ISingletonService
{
    private readonly IScopedService _scopedService;

    public SingletonService(IScopedService svc)
    {
        _scopedService = svc;
    }
}

program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton<ISingletonService, SingletonService>();
builder.Services.AddScoped<IScopedService, ScopedService>();

Application

Understanding of code.

We have created two classes, Singelton and Scoped, and we have injected scope into the singleton service.

When we run the application, we will get the below error.

Run Application

Understanding Error

We have three types of service lifetimes.

  1. Singleton: they’re created the first time they’re requesting, and every time after that, the same instance will be reused
  2. Scoped: they’re created once per the request (connection)

Transients are created every time you request them from the DI container

Here, we are trying to inject an object that will be created on each user scope request so that it is required to be disposed of after each and every request. Here, the Singelton object will never be disposed of, but the scoped object is required to be disposed of, so this is a conflict.

As per the above explanation, we should almost never consume scoped service or transient service from a singleton. You should also avoid consuming transient service from a scoped service.

What happens when you consume a scoped service from a singleton service is known as a captive dependency. It occurs when a service intended to live for a short(er) amount of time gets held by a service that lives for a more extended amount of time. This almost always suggests that you should change something in your design.

Solution

If there is a need to use the Scoped service inside singleton, then we need to create scope manually. A new scope can be created by injecting an IServiceScopeFactory into your singleton service. The IServiceScopeFactory has a CreateScope method, which is used to create new scope instances. IServiceScopeFactory is Singelton in itself, and that’s why it works here.

public interface IScopedService
{
    void Printdata();
}

public interface ISingletonService
{
}

public class SingletonService : ISingletonService
{
    private readonly IServiceScopeFactory _serviceScopeFactory;

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

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

public class ScopedService : IScopedService
{
    public ScopedService()
    {
    }

    public void Printdata()
    {
        Console.WriteLine("Called from scope service from singleton");
        Console.ReadKey();
    }
}

Key Points from the above code.

Code

  1. We Injected IServiceScopeFactory into a singleton class
  2. Create Scope using IServiceScopeFactory
  3. We will get the IServiceScope object
  4. We will use IServiceProvider to get a service object (for a class that is required). IServiceProvider is used to resolve dependencies from the scope.
  5. We have used block, and once Scope service is out of scope, it gets disposed of.

IServiceScopeFactory has CreateScope as a screen below.

CreateScope

Screen for IServiceScope. It has IServiceProvider, and IServiceprovider is used to resolve dependencies.

IServiceProvider

Thanks

Here, we learn how to Inject scoped service inside Singelton

A similar approach is required when we need to use scope service inside the background job in the .Net core.

Use scoped services within a BackgroundService — .NET | Microsoft Learn