# Registering Services

Register your classes with ASP.NET Core's service container in Program.cs - as singleton, scoped or transient - and it creates them and hands them to every controller that asks in its constructor.

Journeyman · ASP.NET Core · C# · Updated November 3, 2026

## After this lesson you can

- Register a class with AddScoped, AddSingleton or AddTransient, and ask for it in a controller's constructor
- Choose a service lifetime (singleton, scoped or transient) for a class

## Key takeaways

- Register a service once in `Program.cs`, such as `builder.Services.AddScoped(_ => new ProductDao(connectionString))`, and any controller can ask for it in its constructor.
- When a class's constructor only asks for other registered services, the type alone is enough: `AddSingleton<UserService>()`.
- An `InvalidOperationException` saying "Unable to resolve service for type" means a type isn't registered: add it to `Program.cs`.
- `AddScoped` makes one object per request, the usual choice for DAOs; `AddSingleton` one for the whole app; `AddTransient` a new one every time.
- Never put one user's data in a singleton: every other user would see it.

In *Constructor Injection*, you passed dependencies in yourself: `new Greeter(new Clock())`. In a web app, ASP.NET Core creates the controllers, so it has to know how to create what they ask for. You tell it, in `Program.cs`, by **registering services**.

## Before: every controller makes its own DAO

Until now, each controller built its DAO from the configuration:

```csharp
public ProductsController(IConfiguration configuration)
{
    productDao = new ProductDao(configuration.GetConnectionString("Northwind")!);
}
```

Every controller that needs products repeats that, and each one is wired to `ProductDao` and to the setting's name.

## Register once, ask anywhere

Register the DAO with the **service container** in `Program.cs`, before `builder.Build()`:

```csharp
string connectionString = builder.Configuration.GetConnectionString("Northwind")!;
builder.Services.AddScoped(_ => new ProductDao(connectionString));
builder.Services.AddSingleton<UserService>();
```

- `AddScoped(_ => new ProductDao(connectionString))` tells the container *how* to make a `ProductDao`: with this method, which passes the connection string.
- `AddSingleton<UserService>()` tells it to make a `UserService` with its constructor. When a class's constructor only asks for other registered services (or nothing), the type alone is enough.

Now a controller just asks:

```csharp
public class ProductsController : Controller
{
    private readonly ProductDao productDao;

    public ProductsController(ProductDao productDao)
    {
        this.productDao = productDao;
    }
}
```

When a request comes in, ASP.NET Core sees the constructor wants a `ProductDao`, finds the registration, makes one, and passes it in. The controller no longer knows where the connection string comes from.

Asking for a type that isn't registered is an error when the controller is created:

```text
InvalidOperationException: Unable to resolve service for type 'NorthwindWeb.Data.ProductDao' while attempting to activate 'NorthwindWeb.Controllers.ProductsController'.
```

If you see it, the service is missing from `Program.cs`.

## Lifetimes

Each registration says how long one object lives:

| Method | One object per | Use for |
|---|---|---|
| `AddSingleton` | The whole app, shared by every request | Things with no per-request data, that are costly to make or must be shared: a `UserService` that holds a list |
| `AddScoped` | Request: every class in one request shares it | DAOs and services that work with the database: the usual choice |
| `AddTransient` | Every time it's asked for | Small, light helpers with no state |

A **scoped** `ProductDao` is created when a request needs it and thrown away when the request ends. Two controllers or services in the same request get the same one.

A **singleton** is shared by everyone, at the same time. Never put one user's data in a singleton: every other user would see it.

## Services in views

A view can ask for a service too, with `@inject`:

```cshtml
@inject NorthwindWeb.Data.ProductDao ProductDao

<p>Northwind sells @ProductDao.Count() products.</p>
```

Use it sparingly: data for a page usually belongs in the model the controller passes.

## The services you've already used

`AddControllersWithViews()` in `Program.cs` registers MVC's own services, and `IConfiguration`, which you've asked for since *Controllers and Actions*, was registered by `WebApplication.CreateBuilder`. Everything in ASP.NET Core comes from this container.

## Check yourself

2 questions to try on the lesson page.

## So what

Register each service once, ask for it by type, and ASP.NET Core does the creating. Controllers stay short, every dependency is visible in its constructor, and tests can hand in fakes.

## Key terms

- Dependency Injection
- AddScoped, AddTransient, AddSingleton
- Service Lifetime
- @inject

## Cheat sheets

- [Registering Services](https://www.ask-codey.com/cheat-sheets/dependency-injection-in-aspnet-core-registering-services-cheat-sheet)

Source: https://www.ask-codey.com/lessons/dependency-injection-in-aspnet-core-registering-services
