Skip to content

Commit fa427a1

Browse files
Merge pull request #47863 from MicrosoftDocs/NEW-aspnetcore-dependency-injection
[NEW MODULE] (Publishing release branch) - Configure Services in ASP.NET Core
2 parents 320fa93 + 2b70a0a commit fa427a1

17 files changed

+584
-0
lines changed

.openpublishing.publish.config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@
6666
"branch": "solution",
6767
"branch_mapping": {}
6868
},
69+
{
70+
"path_to_root": "aspnetcore-snippets",
71+
"url": "https://github.com/MicrosoftDocs/mslearn-aspnetcore-snippets",
72+
"branch": "main",
73+
"branch_mapping": {}
74+
},
6975
{
7076
"path_to_root": "active-directory-b2c-msal-node-sign-in-sign-out-webapp",
7177
"url": "https://github.com/Azure-Samples/active-directory-b2c-msal-node-sign-in-sign-out-webapp",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
### YamlMime:ModuleUnit
2+
uid: learn.configure-dependency-injection.intro
3+
title: Introduction
4+
metadata:
5+
title: Introduction
6+
description: Introduction to ASP.NET Core dependency injection.
7+
ms.date: 11/7/2024
8+
author: camsoper
9+
ms.author: casoper
10+
ms.topic: unit
11+
durationInMinutes: 1
12+
content: |
13+
[!include[](includes/1-introduction.md)]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
### YamlMime:ModuleUnit
2+
uid: learn.configure-dependency-injection.understand-dependency-injection
3+
4+
title: Understand dependency injection
5+
6+
metadata:
7+
title: Understand dependency injection
8+
9+
description: This unit explains what dependency injection is and how it works in an ASP.NET Core app.
10+
ms.date: 11/7/2024
11+
author: camsoper
12+
ms.author: casoper
13+
ms.topic: unit
14+
ai-usage: ai-assisted
15+
durationInMinutes: 10
16+
content: |
17+
[!include[](includes/2-understand-dependency-injection.md)]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
### YamlMime:ModuleUnit
2+
uid: learn.configure-dependency-injection.exercise-register-services
3+
4+
5+
title: Exercise - Register and consume services
6+
7+
metadata:
8+
title: Exercise - Register and consume services
9+
10+
description: This unit is an exercise that demonstrates how to register and consume services in an ASP.NET Core app.
11+
ms.date: 11/7/2024
12+
author: camsoper
13+
ms.author: casoper
14+
ms.topic: unit
15+
durationInMinutes: 10
16+
content: |
17+
[!include[](includes/3-exercise-register-services.md)]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
### YamlMime:ModuleUnit
2+
uid: learn.configure-dependency-injection.service-lifetimes
3+
4+
title: Service lifetimes
5+
6+
metadata:
7+
title: Service lifetimes
8+
9+
description: This unit explains what service lifetimes are in an ASP.NET Core app.
10+
ms.date: 11/7/2024
11+
author: camsoper
12+
ms.author: casoper
13+
ms.topic: unit
14+
durationInMinutes: 5
15+
content: |
16+
[!include[](includes/4-service-lifetimes.md)]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
### YamlMime:ModuleUnit
2+
uid: learn.configure-dependency-injection.exercise-explore-service-lifetime
3+
4+
5+
title: Exercise - Explore service lifetimes
6+
7+
metadata:
8+
title: Exercise - Explore service lifetimes
9+
10+
description: This unit is an exercise that explores configuring service lifetimes an ASP.NET Core app.
11+
ms.date: 11/7/2024
12+
author: camsoper
13+
ms.author: casoper
14+
ms.topic: unit
15+
durationInMinutes: 10
16+
content: |
17+
[!include[](includes/5-exercise-explore-service-lifetime.md)]
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
### YamlMime:ModuleUnit
2+
uid: learn.configure-dependency-injection.knowledge-check
3+
title: Knowledge check
4+
metadata:
5+
title: Knowledge check
6+
description: Knowledge check for ASP.NET Core dependency injection.
7+
ms.date: 11/7/2024
8+
author: CamSoper
9+
ms.author: casoper
10+
ms.topic: unit
11+
durationInMinutes: 5
12+
quiz:
13+
title: Check your understanding
14+
questions:
15+
- content: "Which of the following statements is true regarding dependency injection?"
16+
choices:
17+
- content: "Dependency injection is a design pattern that allows you to loosely couple your app's dependencies."
18+
isCorrect: false
19+
explanation: "Incorrect. This is a true statement, but there's a better answer."
20+
- content: "ASP.NET Core comes with a built-in service container that supports dependency injection."
21+
isCorrect: false
22+
explanation: "Incorrect. This is a true statement, but there's a better answer."
23+
- content: "ASP.NET Core's service container supports three service lifetimes: transient, scoped, and singleton."
24+
isCorrect: false
25+
explanation: "Incorrect. This is a true statement, but there's a better answer."
26+
- content: "All of the above."
27+
isCorrect: true
28+
explanation: "Correct! All of the above statements are true."
29+
- content: "What is the behavior of a service registered with the **singleton** service lifetime?"
30+
choices:
31+
- content: "A new instance of the service is created for each request."
32+
isCorrect: false
33+
explanation: "Incorrect. This is the behavior of a scoped service."
34+
- content: "A single instance of the service is created for the lifetime of the app."
35+
isCorrect: true
36+
explanation: "Correct! Services registered with the singleton lifetime are created once and reused for the lifetime of the app."
37+
- content: "A new instance of the service is created each time an instance is requested."
38+
isCorrect: false
39+
explanation: "Incorrect. This is the behavior of a transient service."
40+
- content: "None of the above."
41+
isCorrect: false
42+
explanation: "Incorrect. One of the above choices is correct."
43+
- content: "What is the behavior of a service registered with the **scoped** service lifetime?"
44+
choices:
45+
- content: "A new instance of the service is created for the current scope, like the current request."
46+
isCorrect: true
47+
explanation: "Correct! ASP.NET Core creates a scope for each request, so scoped services can be reused within the scope of the request."
48+
- content: "A single instance of the service is created for the lifetime of the app."
49+
isCorrect: false
50+
explanation: "Incorrect. This is the behavior of a singleton service."
51+
- content: "A new instance of the service is created each time an instance is requested."
52+
isCorrect: false
53+
explanation: "Incorrect. This is the behavior of a transient service."
54+
- content: "None of the above."
55+
isCorrect: false
56+
explanation: "Incorrect. One of the above choices is correct."
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
### YamlMime:ModuleUnit
2+
uid: learn.configure-dependency-injection.summary
3+
title: Summary
4+
metadata:
5+
title: Summary
6+
description: This unit is the summary of the module.
7+
ms.date: 11/7/2024
8+
author: camsoper
9+
ms.author: casoper
10+
ms.topic: unit
11+
durationInMinutes: 3
12+
content: |
13+
[!include[](includes/7-summary.md)]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
When an ASP.NET Core app receives an HTTP request, the code handling the request sometimes needs to access other services. For example, a Blazor component might need to access a service that fetches data from a database. ASP.NET Core uses a built-in dependency injection (DI) container to manage the services that an app uses.
2+
3+
## Example scenario
4+
5+
Suppose you're an entry-level ASP.NET Core developer at a small company. Your team is building a new web app. The requirements accessing and displaying a customer welcome message to the user on the welcome page. Your team lead asked you to configure the necessary services for accessing the data so they can be used from the web UI components.
6+
7+
## What will we be doing?
8+
9+
In this module, you use the .NET SDK to create a boilerplate ASP.NET Core web application. After ensuring it runs correctly, you'll implement an in-memory service to generate the welcome message. You'll then use the built-in dependency injection container to inject the service where needed.
10+
11+
## What is the main goal?
12+
13+
By the end of the module, you'll be able to create an ASP.NET Core web application that uses the built-in dependency injection container to manage services. You'll also be able to describe the benefits of using dependency injection in an ASP.NET Core app.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
ASP.NET Core apps often need to access the same services across multiple components. For example, several components might need to access a service that fetches data from a database. ASP.NET Core uses a built-in dependency injection (DI) container to manage the services that an app uses.
2+
3+
## Dependency injection and Inversion of Control (IoC)
4+
5+
The dependency injection pattern is a form of Inversion of Control (IoC). In the dependency injection pattern, a component receives its dependencies from external sources rather than creating them itself. This pattern decouples the code from the dependency, which makes code easier to test and maintain.
6+
7+
Consider the following *Program.cs* file:
8+
9+
:::code language="csharp" source="~/../aspnetcore-snippets/learn-pr/aspnetcore/configure-dependency-injection/introduction.cs" id="snippet_program" highlight="8,11-16":::
10+
11+
And the following *PersonService.cs* file:
12+
13+
:::code language="csharp" source="~/../aspnetcore-snippets/learn-pr/aspnetcore/configure-dependency-injection/introduction.cs" id="snippet_personservice":::
14+
15+
To understand the code, start with the highlighted `app.MapGet` code. This code maps HTTP GET requests for the root URL (`/`) to a delegate that returns a greeting message. The delegate's signature defines an `PersonService` parameter named `personService`. When the app runs and a client requests the root URL, the code inside the delegate *depends* on the `PersonService` service to get some text to include in the greeting message.
16+
17+
Where does the delegate get the `PersonService` service? It's implicitly provided by the service container. The highlighted `builder.Services.AddSingleton<PersonService>()` line tells the service container to create a new instance of the `PersonService` class when the app starts, and to provide that instance to any component that needs it.
18+
19+
Any component that needs the `PersonService` service can declare a parameter of type `PersonService` in its delegate signature. The service container will automatically provide an instance of the `PersonService` class when the component is created. The delegate doesn't create the `PersonService` instance itself, it just uses the instance that the service container provides.
20+
21+
## Interfaces and dependency injection
22+
23+
To avoid dependencies on a specific service implementation, you can instead configure a service for a specific interface and then depend just on the interface. This approach gives you the flexibility to swap out the service implementation, which makes the code more testable and easier to maintain.
24+
25+
Consider an interface for the `PersonService` class:
26+
27+
:::code language="csharp" source="~/../aspnetcore-snippets/learn-pr/aspnetcore/configure-dependency-injection/introduction.cs" id="snippet_personserviceinterface":::
28+
29+
This interface defines the single method, `GetPersonName`, that returns a `string`. This `PersonService` class implements the `IPersonService` interface:
30+
31+
:::code language="csharp" source="~/../aspnetcore-snippets/learn-pr/aspnetcore/configure-dependency-injection/introduction.cs" id="snippet_personserviceimplementation":::
32+
33+
Instead of registering the `PersonService` class directly, you can register it as an implementation of the `IPersonService` interface:
34+
35+
:::code language="csharp" source="~/../aspnetcore-snippets/learn-pr/aspnetcore/configure-dependency-injection/introduction.cs" id="snippet_programinterfaces" highlight="3,7":::
36+
37+
This example *Program.cs* differs from the previous example in two ways:
38+
39+
- The `PersonService` instance is registered as an *implementation* of the `IPersonService` interface (as opposed to registering the `PersonService` class directly).
40+
- The delegate signature now expects an `IPersonService` parameter instead of a `PersonService` parameter.
41+
42+
When the app runs and a client requests the root URL, the service container provides an instance of the `PersonService` class because it's registered as the implementation of the `IPersonService` interface.
43+
44+
> [!TIP]
45+
> Think of `IPersonService` as a contract. It defines the methods and properties that an implementation **must** have. The delegate wants an instance of `IPersonService`. It doesn't care at all about the underlying implementation, only that the instance has the methods and properties defined in the contract.
46+
47+
## Testing with dependency injection
48+
49+
Using interfaces makes it easier to test components in isolation. You can create a mock implementation of the `IPersonService` interface for testing purposes. When you register the mock implementation in the test, the service container provides the mock implementation to the component being tested.
50+
51+
For example, say that instead of returning a hard-coded string, the `GetPersonName` method in the `PersonService` class fetches the name from a database. To test the component that depends on the `IPersonService` interface, you can create a mock implementation of the `IPersonService` interface that returns a hard-coded string. The component being tested doesn't know the difference between the real implementation and the mock implementation.
52+
53+
Also suppose your app maps an API endpoint that returns a greeting message. The endpoint depends on the `IPersonService` interface to get the name of the person to greet. The code that registers the `IPersonService` service and maps the API endpoint might look like this:
54+
55+
:::code language="csharp" source="~/../aspnetcore-snippets/learn-pr/aspnetcore/configure-dependency-injection/introduction.cs" id="snippet_test_program" highlight="3,7-10":::
56+
57+
This is similar the previous example with `IPersonService`. The delegate expects an `IPersonService` parameter, which the service container provides. As mentioned earlier, assume that the `PersonService` that implements the interface fetches the name of the person to greet from a database.
58+
59+
Now consider the following XUnit test that tests the same API endpoint:
60+
61+
> [!TIP]
62+
> Don't worry if you're not familiar with XUnit or Moq. Writing unit tests is outside the scope of this module. This example is just to illustrate how dependency injection can be used in testing.
63+
64+
:::code language="csharp" source="~/../aspnetcore-snippets/learn-pr/aspnetcore/configure-dependency-injection/introduction.cs" id="snippet_test_personservice" highlight="20-21,27":::
65+
66+
The preceding test:
67+
68+
- Creates a mock implementation of the `IPersonService` interface that returns a hard-coded string.
69+
- Registers the mock implementation with the service container.
70+
- Creates an HTTP client to make a request to the API endpoint.
71+
- Asserts that the response from the API endpoint is as expected.
72+
73+
The test doesn't care how the `PersonService` class gets the name of the person to greet. It only cares that the name is included in the greeting message. The test uses a mock implementation of the `IPersonService` interface to isolate the component being tested from the real implementation of the service.

0 commit comments

Comments
 (0)