How to inject services in ASP.NET Core dependency injection

Published: Monday 30 June 2025

Services can be injected into minimal APIs, controllers, Razor pages and views as well as middleware in an ASP.NET Core app.

Adding services to dependency injection

Before you can do that, you need to add the services to the IServiceCollection instance.

You can either add a service as a standalone class. Or you can create a class that implements an interface and add that interface as a service.

These are the services we will be adding:

// CategoryStorageService.cs
public class CategoryStorageService
{
	public List<CategoryTypeDto> Types { get; } = new()
	{
		new(1, "Electronics"),
		new(2, "Computers"),
		new(3, "Home and Kitchen"),
		new(4, "Software")
	};
}

public interface ICategoryService
{
	CategoryTypeDto? GetCategoryType(int id);
}

public class CategoryService : ICategoryService
{
	public CategoryStorageService _categoryStorageService;

	public CategoryService(
		CategoryStorageService categoryStorageService
		)
	{
		_categoryStorageService = categoryStorageService;
	}

	public CategoryTypeDto? GetCategoryType(int id) => 
		_categoryStorageService.Types.SingleOrDefault(c => c.Id == id);
	}
}

public interface ICategoryRandomService
{
	CategoryTypeDto? RandomCategoryType { get; }
}

public class CategoryRandomService : ICategoryRandomService
{
	public CategoryTypeDto? RandomCategoryType { get; }

	public CategoryRandomService(CategoryStorageService categoryStorageService)
	{
		var random = new Random();
		var randomId = random.Next(1, categoryStorageService.Types.Count + 1);

		RandomCategoryType = categoryStorageService.Types.SingleOrDefault(s => s.Id == randomId);
	}
}

In Program.cs, you add the services by either calling the AddSingleton, AddScoped or AddTransient extension method in the IServiceCollection type:

// Program.cs
builder.Services.AddSingleton<CategoryStorageService>();
builder.Services.AddScoped<ICategoryService, CategoryService>();
builder.Services.AddTransient<ICategoryRandomService, CategoryRandomService>();

Read our article to find out more about singleton, scoped and transient service lifetimes, the different extension methods available and how they behave differently.

Injecting services

There are many areas within ASP.NET Core where you can inject services.

Minimal APIs

It's relatively straight forward to inject services into a minimal API endpoint. All you have to do is to inject the service type that was added into the delegate and the endpoint will resolve the instance.

Here's an example of injecting each of the services and outputting a response:

app.MapGet("/minimal/categories", (
	CategoryStorageService categoryStorageService,
	ICategoryService categoryService,
	ICategoryRandomService categoryRandomService
) => new
{
	CategoryTypes = categoryStorageService.Types,
	Electronics = categoryService.GetCategoryType(1),
	categoryRandomService.RandomCategoryType
});

When we run the /minimal/categories endpoint, the response outputs:

{
	"categoryTypes": [
		{
		  "id": 1,
		  "name": "Electronics"
		},
		{
		  "id": 2,
		  "name": "Computers"
		},
		{
		  "id": 3,
		  "name": "Home and Kitchen"
		},
		{
		  "id": 4,
		  "name": "Software"
		}
	],
	"electronics": {
		"id": 1,
		"name": "Electronics"
	},
	"randomCategoryType": {
		"id": 2,
		"name": "Computers"
	}
}

Because the CategoryRandomService class has been set up with a transient service lifetime, it will create a new instance every time it's injected. As we are storing a RandomCategoryType property with a random category every time the CategoryRandomService class is initialised, we would expect the RandomCategoryType property to populate a different category every time, unless it randomly selects the same category on successive occassions.

Controllers

When using a controller, you inject the service as a parameter in the constructor and store an instance of it as part of a readonly field. This means that the service instance can be used in any member within the controller:

// WebApiController.cs
[Route("api/[controller]")]
[ApiController]
public class WebApiController : ControllerBase
{
	private readonly CategoryStorageService _categoryStorageService;
	private readonly ICategoryService _categoryService;
	private readonly ICategoryRandomService _categoryRandomService;

	public WebApiController(
		CategoryStorageService categoryStorageService,
		ICategoryService categoryService,
		ICategoryRandomService categoryRandomService
		)
	{
		_categoryStorageService = categoryStorageService;
		_categoryService = categoryService;
		_categoryRandomService = categoryRandomService;
	}

	[HttpGet("categories")]
	public IActionResult Categories()
	{
		return Ok(new
		{
			CategoryTypes = _categoryStorageService.Types,
			Electronics = _categoryService.GetCategoryType(1),
			_categoryRandomService.RandomCategoryType
		});
	}
}

When executing the Categories method, it will output a similar response to the minimal API response:

{
	"categoryTypes": [
		{
		  "id": 1,
		  "name": "Electronics"
		},
		{
		  "id": 2,
		  "name": "Computers"
		},
		{
		  "id": 3,
		  "name": "Home and Kitchen"
		},
		{
		  "id": 4,
		  "name": "Software"
		}
	],
	"electronics": {
		"id": 1,
		"name": "Electronics"
	},
	"randomCategoryType": {
		"id": 2,
		"name": "Computers"
	}
}

Razor pages and views

If you use either Razor pages or views, you can use the @inject directive to inject services.

Here's an example of using an ASP.NET Core MVC controller and returning a view:

[Route("[controller]")]
public class MvcController : Controller
{
	[HttpGet("categories")]
	public IActionResult Categories()
	{
		return View();
	}
}
<!-- Categories.cshtml -->
@using RoundTheCode.DI.Services.Category
@inject CategoryStorageService categoryStorageService
@inject ICategoryService categoryService
@inject ICategoryRandomService categoryRandomService
<pre>
Category Types: @Json.Serialize(categoryStorageService.Types)
</pre>
<pre>
Electronics: @Json.Serialize(categoryService.GetCategoryType(1))
</pre>
<pre>
Random Category: @Json.Serialize(categoryRandomService.RandomCategoryType)
</pre>

When running the Categories endpoint in MvcController, it will output the following:

Category Types: [{"id":1,"name":"Electronics"},{"id":2,"name":"Computers"},{"id":3,"name":"Home and Kitchen"},{"id":4,"name":"Software"}]
Electronics: {"id":1,"name":"Electronics"}
Random Category: {"id":1,"name":"Electronics"}

Middleware

Middleware behaves slightly different when inject services. With a middleware class, you can only add singleton service lifetime instances into the constructor. If you add a scoped service lifetime instance, it will throw this runtime error:

InvalidOperationException: 'Cannot resolve scoped service '{Service}' from root provider

You can add a transient service lifetime instance, but it will behave like a singleton service lifetime.

This is because middleware instances have a singleton lifetime. They are only initialised once at application startup and live for the entire lifetime of the application.

If you want to inject scoped or transient service lifetimes into a middleware class, you need to add them as parameters in the InvokeAsync method.

// CategoryMiddleware.cs
public class CategoryMiddleware
{
	private readonly RequestDelegate _next;
	private readonly CategoryStorageService _categoryStorageService;

	public CategoryMiddleware(
		RequestDelegate next, 
		CategoryStorageService categoryStorageService)
	{
		_next = next;
		_categoryStorageService = categoryStorageService;
	}

	public async Task InvokeAsync(
		HttpContext httpContext,
		ICategoryService categoryService,
		ICategoryRandomService categoryRandomService)
	{
		httpContext.Items.Add("GetTypes", _categoryStorageService.Types);
		httpContext.Items.Add("Electronics", categoryService.GetCategoryType(1));
		httpContext.Items.Add("CategoryRandomService", categoryRandomService.RandomCategoryType);

		await _next(httpContext);
	}
}

Primary constructors

It's also possible to inject services using primary constructors. Primary constructors was launched in C# 12 and allows you to inject a service directly into the class declaration.

We've created a new class called CategoryPrimaryService which injects the ICategoryService as part of the class declaration. It then uses the GetCategoryType method in ICategoryService to get details about the software category.

// CategoryPrimaryService.cs
public class CategoryPrimaryService
    (ICategoryService categoryService)
    : ICategoryPrimaryService
{
	public CategoryTypeDto? GetSoftwareCategoryType() =>
		categoryService.GetCategoryType(4);
}

Injecting services into controller endpoints

It's also possible to inject services into endpoints directly. However, depending on whether you are using an MVC and web API controller depends on how it's implemented.

A web API controller usually inherits from the ControllerBase class and includes the [ApiController] attribute. Whereas an MVC controller will inherit the Controller class but does not include the [ApiController] attribute.

This behaviour changes the way you inject services. With a web API endpoint, you can just add the service type as a parameter inside the method:

// WebApiController.cs
[Route("api/[controller]")]
[ApiController]
public class WebApiController : ControllerBase
{
	...

	[HttpGet("computers")]
	public CategoryTypeDto? GetComputersCategory(ICategoryService categoryService)
	{
		return categoryService.GetCategoryType(2);
	}
}

Whereas with an MVC controller endpoint, you have to include the [FromServices] attribute for it to resolve:

// MvcController.cs
[Route("[controller]")]
public class MvcController : Controller
{
	...
	[HttpGet("computers")]
	public IActionResult GetComputersCategory([FromServices] ICategoryService categoryService)
	{
		return Content(JsonSerializer.Serialize(categoryService.GetCategoryType(2)));
	}
}

If you don't include the [FromServices] attribute in an MVC controller endpoint, you get the following exception:

InvalidOperationException: Could not create an instance of type '{Type}'

Watch the video

Watch the video where we show you how to inject services with minimal APIs, web API controllers, MVC controllers and views as well as middleware.

You can also try it out for yourself by downloading the code example and testing the different ways to inject services with ASP.NET Core.