Switching from Singleton to Scoped — Solving Dependency Lifetimes

Jay (Vijayasimha BR)
4 min read3 days ago

--

a young indian woman, wearing crop tops and shorts and orange tennis shoes, carrying large stack of vessels on her head, country side, digital paint, colorful, vibrant, indian village surroundings

Recently, I encountered a situation where a Singleton Dependency simply stopped working.

I have this project called ‘comic book shop’. It’s a simple micro service project that I use as a capstone project in my tutoring lessons. Recently, I discovered an odd scenario where, for some reason, the email service, simply stopped working.

Here is the specific piece of code, that stopped working. Actually, it was working sometimes, and it was not working other times. The issue got worse when I started running the project alone for the purpose of debugging, instead of the usual practice of running it as part of the entire solution which has a total of 8 projects.

builder.Services.AddSingleton(new EmailService(optionBuilder.Options));

Here is the new code, that made sure this works, 100 % of the time.

builder.Services.AddScoped<IEmailService, EmailService>();

note: more specific details of the changes, can be found in this blog post.

Now, although I do plenty of .NET, I am not exactly an expert on dependency injection. So, I decided to have a discussion with GitHub CoPilot Pro about these two types of dependency injection. Here is what I found out.

1. Why Did Your Project Start Working After Switching to AddScoped?

AddSingleton:

  • Lifetime: The AddSingleton method registers a service as a singleton. This means that only one instance of the service is created and shared throughout the application's lifetime.
  • Usage: Singleton services are best suited for stateless services or services that maintain a shared state that should persist for the application’s lifetime.

AddScoped:

  • Lifetime: The AddScoped method registers a service with a scoped lifetime. This means that a new instance of the service is created for each HTTP request.
  • Usage: Scoped services are ideal for services that require dependency injection and should not be shared across multiple requests. They are also suitable for services that need to maintain a state within the scope of a single request.

Looking at this, it seems like, perhaps, the Singleton was reaching its end of cycle, I think. It started as usual, and then, ended. So, when new events were raised (the email service is designed to work hand in hand with Azure Service Bus and process queue messages), the Email Service was not running (having finished its complete lifetime), and hence, the queue was not getting processed.

The main problem is that I myself am not sure how to use Singleton properly. So, the fault most definitely lies with me.

However, I also noticed that my Email Service does meet the essentials of using a scoped DI.

  1. Although the email service was doing an identical task, processing queues, the context and data itself was different. I would like it if the service was not shared across different requests.
  2. Further, there is the issue of state. Each service has it’s own state, for example, data that needed processing, and also connection string of the queue.
  3. Further, singleton makes the project more efficient (no need to create a new instance for every request). However, my project is a learning, student project and is not meant for production use. So, some overhead is perfectly okay, I imagine.

Ultimately, the AI says the following, which aligns with my own views.

Switching from Singleton to Scoped resolved the issues because:

  • State Management: Scoped instances ensure no shared state between requests, preventing state-related issues.
  • Concurrency: No concurrency issues as each request gets its own instance.
  • Dependency Lifecycle: Proper lifecycle management of dependencies, avoiding issues with disposed or mismatched lifetimes.
  • Configuration: Correct per-request initialization and configuration.

So, there is that. I probably should (and will) dig deeper into these different types of dependency injections. When I do that, I will post my thoughts here, of course.

You can find the project documentation, here.

Comic Book Shop — Documentation — Version 1.0.3 | comicbookshop

You can find the project GitHub Board, here.

Roadmap · Comic Book Shop

There are always bugs and fixes awaiting attention. I welcome you to contribute and become involved.

I work as a coding tutor. You can hire me on Upwork, Fiverr and Codementor. You can also book a session on calendly, and visit my website. Also, video tutorials on my YouTube Channel.

--

--

No responses yet