How to Register Services in ASP.NET Core Dependency Injection
Dependency Injection is a core feature of ASP.NET Core applications. Before ASP.NET Core can provide a service to a controller, Razor Page, Blazor component, or another class, the service normally needs to be registered with the Dependency Injection container.
In this tutorial, you will learn how to register services in ASP.NET Core,
how interface-to-implementation registration works, and when to use
AddTransient, AddScoped, and
AddSingleton.
Program.cs, and then inject
it wherever your application needs it.
π What Does Service Registration Mean?
Service registration tells the ASP.NET Core Dependency Injection container how to create a particular service when the application requests it.
For example:
builder.Services.AddScoped<MessageService>();
This tells ASP.NET Core that MessageService is a service that
can be requested through Dependency Injection.
Once registered, a class can request the service through its constructor.
public class HomeController : Controller
{
private readonly MessageService _messageService;
public HomeController(MessageService messageService)
{
_messageService = messageService;
}
}
ποΈ Where Do You Register Services?
In modern ASP.NET Core applications, services are commonly registered in
Program.cs.
For example:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<MessageService>();
var app = builder.Build();
The builder.Services collection is used to register services
before the application is built.
βοΈ Registering a Service Without an Interface
The simplest registration uses the service type directly.
First, create a service:
public class MessageService
{
public string GetMessage()
{
return "Hello from MessageService!";
}
}
Register it:
builder.Services.AddScoped<MessageService>();
The service can now be injected into another class:
public class HomeController : Controller
{
private readonly MessageService _messageService;
public HomeController(MessageService messageService)
{
_messageService = messageService;
}
public IActionResult Index()
{
return Content(_messageService.GetMessage());
}
}
π Registering an Interface and Implementation
In real applications, you will often use an interface for your service.
Create an interface:
public interface IMessageService
{
string GetMessage();
}
Create the implementation:
public class MessageService : IMessageService
{
public string GetMessage()
{
return "Hello from IMessageService!";
}
}
Register the interface and implementation:
builder.Services.AddScoped<IMessageService, MessageService>();
Now a class can request IMessageService rather than the
concrete MessageService class.
public class HomeController : Controller
{
private readonly IMessageService _messageService;
public HomeController(IMessageService messageService)
{
_messageService = messageService;
}
public IActionResult Index()
{
return Content(_messageService.GetMessage());
}
}
β±οΈ The Three Service Lifetimes
ASP.NET Core provides three commonly used service lifetimes:
- Transient β a new service instance is created each time the service is requested.
- Scoped β the service instance is generally reused within the same scope.
- Singleton β the same service instance is reused for the lifetime of the application.
These lifetimes are selected using the corresponding registration methods.
π AddTransient
Use AddTransient when you want a new service instance each
time the service is requested.
builder.Services.AddTransient<IMessageService, MessageService>();
Transient services are useful for lightweight, stateless services where you do not need to preserve an instance between requests.
π¦ AddScoped
Use AddScoped to register a service with a scoped lifetime.
builder.Services.AddScoped<IMessageService, MessageService>();
Scoped services are commonly used for services whose lifetime should be associated with a particular request or scope.
DbContext are commonly registered with a scoped lifetime.
βΎοΈ AddSingleton
Use AddSingleton when one instance should be used throughout
the application's lifetime.
builder.Services.AddSingleton<IMessageService, MessageService>();
Because the instance can live for the entire application lifetime, singleton services should be designed carefully, especially when they contain mutable state.
π AddTransient vs AddScoped vs AddSingleton
| Lifetime | Registration | Typical Behavior |
|---|---|---|
| Transient | AddTransient | New instance when requested |
| Scoped | AddScoped | Instance reused within a scope |
| Singleton | AddSingleton | One instance for application lifetime |
π§© Registering Multiple Services
You can register many different services in Program.cs.
builder.Services.AddTransient<IEmailService, EmailService>();
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddSingleton<IApplicationSettings, ApplicationSettings>();
Each registration tells the Dependency Injection container which implementation should be supplied when the corresponding interface is requested.
π§ Registering a Service with Dependencies
A service can also depend on another service.
For example:
public class OrderService
{
private readonly IMessageService _messageService;
public OrderService(IMessageService messageService)
{
_messageService = messageService;
}
public string CreateOrder()
{
return _messageService.GetMessage();
}
}
Register both services:
builder.Services.AddScoped<IMessageService, MessageService>();
builder.Services.AddScoped<IOrderService, OrderService>();
ASP.NET Core can then resolve the dependency chain automatically.
π Registering a Service Using a Factory
Sometimes you need more control over how a service is created. ASP.NET Core allows a factory delegate to be used during registration.
builder.Services.AddScoped<IMessageService>(serviceProvider =>
{
return new MessageService();
});
The factory receives the application's service provider, allowing you to resolve other registered services when creating the object.
π Registering Configuration-Based Services
A service can also be created using application configuration.
For example, suppose an application contains a configuration value:
builder.Services.AddSingleton<AppSettings>(serviceProvider =>
{
var configuration =
serviceProvider.GetRequiredService<IConfiguration>();
return new AppSettings
{
ApplicationName =
configuration["ApplicationName"]
};
});
This approach is useful when a service needs configuration values while being registered.
π¨ Common Service Registration Mistakes
1. Forgetting to Register a Service
If a class requests a service that has not been registered, dependency resolution can fail at runtime.
public HomeController(IMessageService messageService)
{
_messageService = messageService;
}
Make sure the corresponding registration exists:
builder.Services.AddScoped<IMessageService, MessageService>();
2. Registering the Wrong Implementation
Make sure the implementation matches the interface being requested.
builder.Services.AddScoped<IMessageService, MessageService>();
3. Choosing the Wrong Lifetime
Service lifetimes affect how instances are created and reused. Choosing a lifetime without considering the service's dependencies and state can cause problems.
We will cover the differences between these lifetimes in detail in the next supporting article.
π Related Dependency Injection Articles
ποΈ ASP.NET Core Dependency Injection Explained with Examples (Beginner to Advanced)
π What Is Dependency Injection in ASP.NET Core? A Beginner's Guide
π Singleton vs Scoped vs Transient in ASP.NET Core: What's the Difference?
π§© How to Use Constructor Injection in ASP.NET Core
π How to Inject Services into Controllers, Razor Pages, and Blazor
π¨ Common Dependency Injection Errors in ASP.NET Core and How to Fix Them
π Advanced Dependency Injection in ASP.NET Core: Factory, Keyed Services, and Service Lifetimes
π― Conclusion
Registering services is one of the first things you need to understand when working with Dependency Injection in ASP.NET Core.
The three most common registration methods are
AddTransient, AddScoped, and
AddSingleton. You can also register interfaces with their
implementations and allow ASP.NET Core to resolve dependencies
automatically.
Now that you know how to register services, the next important topic is understanding their lifetimes:
π Singleton vs Scoped vs Transient in ASP.NET Core: What's the Difference?
Ask AlgoLassi and get an answer plus the tutorials worth studying next.
π¬ Comments
Sign in with Google to publish immediately, or comment anonymously and wait for approval.
Comments will appear here when available.