Using Scoped Services From Singletons in ASP.NET Core
Tags: dependencyinjection, aspnetcore, dotnet, programming
Published: 2024-02-19 15:27:59

---
title: Using Scoped Services From Singletons in ASP.NET Core
published: true
date: 2024-02-17 00:00:00 UTC
tags: dependencyinjection,aspnetcore,dotnet,programming
canonical_url: https://www.milanjovanovic.tech/blog/using-scoped-services-from-singletons-in-aspnetcore
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v22occwgdrij9jhtmf6r.png
---
Did you ever need to inject a scoped service into a singleton service?

I often need to resolve a scoped service, like the EF Core `DbContext`, in a background service.

Another example is when you need to resolve a scoped service in ASP.NET Core middleware.

If you ever tried this, you were probably greeted with an exception similar to this one:

```csharp
System.InvalidOperationException: Cannot consume scoped service 'Scoped' from singleton 'Singleton'.
```

Today, I'll explain how you can solve this problem and safely use scoped services from within singletons in ASP.NET Core.

## ASP.NET Core Service Lifetimes

ASP.NET Core has three [service lifetimes](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-lifetimes):

- Transient
- Singleton
- Scoped

[Transient services](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#transient) are created each time they're requested from the service container.

[Scoped services](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scoped) are created once within the scope's lifetime. For ASP.NET Core applications, a new scope is created for each request. This is how you can resolve scoped services within a given request.

ASP.NET Core applications also have a root `IServiceProvider` used to resolve [singleton services.](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#singleton)

So, what can we do if resolving a scoped service from a singleton throws an exception?

## The Solution - `IServiceScopeFactory`

What if you want to resolve a scoped service inside a [background service](https://www.milanjovanovic.tech/blog/running-background-tasks-in-asp-net-core)?

You can create a new scope (`IServiceScope`) with its own `IServiceProvider` instance. The scoped `IServiceProvider` can be used to resolve scoped services. When the scope is disposed, all disposable services created within that scope are also disposed.

Here's an example of using the `IServiceScopeFactory` to create a new `IServiceScope`. We're using the scope to resolve the `ApplicationDbContext`, which is a scoped service.

The `BackgroundJob` is registered as a singleton when calling `AddHostedService<BackgroundJob>`.

```csharp
public class BackgroundJob(IServiceScopeFactory serviceScopeFactory)
    : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using IServiceScope scope = serviceScopeFactory.CreateScope();

        var dbContext = scope
            .ServiceProvider
            .GetRequiredService<ApplicationDbContext>();

        // Do some background processing with the EF database context.
        await DoWorkAsync(dbContext);
    }
}
```

## Scoped Services in Middleware

What if you want to use a scoped service in [ASP.NET Core middleware](https://www.milanjovanovic.tech/blog/3-ways-to-create-middleware-in-asp-net-core)?

Middleware is constructed once per application lifetime.

If you try injecting a scoped service, you'll get an exception:

```csharp
System.InvalidOperationException: Cannot resolve scoped service 'Scoped' from root provider.
```

There are two ways to get around this.

First, you could use the previous approach with creating a new scope using `IServiceScopeFactory`. You'll be able to resolve scoped services. But, they won't share the same lifetime as the other scoped service in the same request. This could even be a problem depending on your requirements.

Is there a better way?

Middleware allows you to inject scoped services in the `InvokeAsync` method. The injected services will use the current request's scope, so they'll have the same lifetime as any other scoped service.

```csharp
public class ConventionalMiddleware(RequestDelegate next)
{
    public async Task InvokeAsync(
        HttpContext httpContext,
        IMyScopedService scoped)
    {
        scoped.DoSomething();

        await _next(httpContext);
    }
}
```

## `IServiceScopeFactory` vs. `IServiceProvider`

You might see examples using the `IServiceProvider` to create a scope instead of the `IServiceScopeFactory`.

What's the difference between these two approaches?

The [`CreateScope` method from `IServiceProvider`](https://github.com/aspnet/DependencyInjection/blob/94b9cc9ace032f838e068702cc70ce57cc883bc7/src/DI.Abstractions/ServiceProviderServiceExtensions.cs#L125)resolves an `IServiceScopeFactory` instance and calls `CreateScope()` on it:

```csharp
public static IServiceScope CreateScope(this IServiceProvider provider)
{
    return provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}
```

So, if you want to use the `IServiceProvider` directly to create a scope, that's fine.

However, the `IServiceScopeFactory` is a more direct way to achieve the desired result.

## Summary

Understanding the difference between Transient, Scoped, and Singleton lifetimes is crucial for managing dependencies in ASP.NET Core applications.

The `IServiceScopeFactory` provides a solution when you need to resolve scoped services from singletons. It allows you to create a new scope, which you can use to resolve scoped services.

In middleware, we can inject scoped services into the `InvokeAsync` method. This also ensures the services use the current request's scope and lifecycle.

Thanks for reading, and I'll see you next week!

* * *

P.S. Whenever you're ready, there are 3 ways I can help you:

1. [**Modular Monolith Architecture:**](https://www.milanjovanovic.tech/modular-monolith-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting) This in-depth course will transform the way you build monolith systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario. [**Join the wait list here.**](https://www.milanjovanovic.tech/modular-monolith-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting)

2. [**Pragmatic Clean Architecture:**](https://www.milanjovanovic.tech/pragmatic-clean-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting) This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture. [**Join 2,400+ students here.**](https://www.milanjovanovic.tech/pragmatic-clean-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting)

3. [**Patreon Community:**](https://www.patreon.com/milanjovanovic) Think like a senior software engineer with **access to the source code** I use in my YouTube videos and **exclusive discounts** for my courses. [**Join 1,050+ engineers here.**](https://www.patreon.com/milanjovanovic)